杰克逊如何将JsonNode转换为ArrayNode而不需要强制转换?

165

我正在将我的JSON库从org.json切换到Jackson,并且希望迁移以下代码:

JSONObject datasets = readJSON(new URL(DATASETS));
JSONArray datasetArray =  datasets.getJSONArray("datasets");

现在在杰克逊我有以下内容:

ObjectMapper m = new ObjectMapper();
JsonNode datasets = m.readTree(new URL(DATASETS));      
ArrayNode datasetArray = (ArrayNode)datasets.get("datasets");

然而,我不喜欢那里的演员阵容,是否有可能出现ClassCastException? 在org.json中是否有与getJSONArray等效的方法,以便在它不是数组的情况下进行适当的错误处理?


很遗憾,我无法使用完整的映射,因为数据没有固定的字段名称。 - Konrad Höffner
1
如果字段名称来自有限的集合,您可能希望定义一个包含所有字段的类,并使用反序列化器的 FAIL_ON_UNKNOWN_PROPERTIES 功能,以便在未使用的字段中仅返回 null。但是,当然,只有在字段名称集合相对有限的情况下才是一个选项。 - fvu
嗯,我觉得这个解决方案并不是最适合我的情况,但如果我将来遇到已知限制集的问题,我会记住它的! - Konrad Höffner
5个回答

314
是的,Jackson手动解析器的设计与其他库非常不同。特别是,您会注意到JsonNode具有大多数您通常与其他API中的数组节点相关联的函数。因此,您无需将其转换为ArrayNode即可使用。以下是一个示例:

JSON:

{
    "objects" : ["One", "Two", "Three"]
}

代码:

final String json = "{\"objects\" : [\"One\", \"Two\", \"Three\"]}";

final JsonNode arrNode = new ObjectMapper().readTree(json).get("objects");
if (arrNode.isArray()) {
    for (final JsonNode objNode : arrNode) {
        System.out.println(objNode);
    }
}

输出:

"一"
"二"
"三"

请注意使用isArray来验证节点在迭代之前是否实际上是一个数组。如果您对数据结构非常有信心,则不需要进行检查,但如果需要的话,这个检查是可用的(这与大多数其他JSON库没有什么不同)。


我可以知道为什么在这行代码中使用了“final”关键字吗:“for (final JsonNode objNode : arrNode) ”? - Anthony Vinay
2
@AnthonyVinay 使变量不可变;许多程序员认为这是一种良好的编码实践。另请参见讨论! - Jacob van Lingen

26

在Java 8中,你可以像这样实现:

import java.util.*;
import java.util.stream.*;

List<JsonNode> datasets = StreamSupport
    .stream(obj.get("datasets").spliterator(), false)
    .collect(Collectors.toList())

我想知道为什么在Spliterator中他们没有添加一个stream()方法。 - Marinos An

5

我想最终您希望通过迭代ArrayNode来使用数据。为此:

Iterator<JsonNode> iterator = datasets.withArray("datasets").elements();
while (iterator.hasNext()) 
        System.out.print(iterator.next().toString() + " "); 

或者,如果您喜欢流和Lambda函数:

import com.google.common.collect.Streams;
Streams.stream(datasets.withArray("datasets").elements())
    .forEach( item -> System.out.print(item.toString()) )

1
通过调用JsonNode的iterator()方法获取迭代器,然后继续进行...
  JsonNode array = datasets.get("datasets");

  if (array.isArray()) {
      Iterator<JsonNode> itr = array.iterator();
      /* Set up a loop that makes a call to hasNext().
      Have the loop iterate as long as hasNext() returns true.*/
      while (itr.hasNext()) {
          JsonNode item=itr.next();
          // do something with array elements
      }
  }

1

是否有一种与org.json中的getJSONArray等效的方法,以便在它不是数组的情况下具有适当的错误处理?

这取决于您的输入;即从URL获取的内容。如果“datasets”属性的值是关联数组而不是普通数组,则会收到ClassCastException。

但是,您的旧版本的正确性也取决于输入。在新版本抛出ClassCastException的情况下,旧版本将抛出JSONException。参考:http://www.json.org/javadoc/org/json/JSONObject.html#getJSONArray(java.lang.String)


啊,好的,那我可以捕获一个ClassCastException异常,谢谢!对我来说,这可能没有一个特定的JsonException异常优雅,但如果没有其他办法,那也还不错。 - Konrad Höffner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接