1. Overview
1.概述
In this article, we’ll explore different ways to extract all the nested keys from a JSON using JsonNode. We aim to traverse through a JSON string and collect key names in a list.
在这篇文章中,我们将探索使用JsonNode从JSON中提取所有嵌套键的不同方法。我们的目标是遍历一个JSON字符串并在一个列表中收集键名。
2. Introduction
2.绪论
The Jackson library uses a tree model to represent JSON data. The tree model provides us with an efficient way to interact with hierarchical data.
Jackson库使用一个树形模型来表示JSON数据。树状模型为我们提供了一种与分层数据交互的有效方式。
JSON objects are represented as nodes in the tree model. This makes it easier to perform CRUD operations on JSON content.
JSON对象在树状模型中被表示为节点。这使得对JSON内容进行CRUD操作更加容易。
2.1. ObjectMapper
2.1.ObjectMapper
We use ObjectMapper class methods to read the JSON content. The ObjectMapper.readTree() method deserializes JSON and builds a tree of JsonNode instances. It takes a JSON source as input and returns the root node of the tree model created. Subsequently, we can use the root node to traverse the entire JSON tree.
我们使用ObjectMapper类方法来读取JSON内容。 ObjectMapper.readTree()方法对JSON进行反序列化并建立一个JsonNode实例的树。它将一个JSON源作为输入,并返回所创建的树模型的根节点。随后,我们可以使用根节点来遍历整个JSON树。
Tree model isn’t limited to reading regular Java objects only. There is a one-to-one mapping between the JSON fields and the tree model. Thereby, each object, whether or not POJO, can be represented as a node.
Hence, we enjoy a flexible approach to represent JSON content as generic nodes.
树模型并不局限于只读取普通的Java对象。在JSON字段和树模型之间有一个一对一的映射。因此,每个对象,无论是否是POJO,都可以被表示为一个节点。
因此,我们享有一种灵活的方法来将JSON内容表示为通用节点。
To learn more, please refer to our article on Jackson ObjectMapper.
要了解更多,请参考我们关于JacksonObjectMapper.的文章。
2.2. JsonNode
2.2. JsonNode
The JsonNode class represents a node in the JSON tree model. It can express JSON data in the following data types: Array, Binary, Boolean, Missing, Null, Number, Object, POJO, String. These data types are defined in the JsonNodeType enum.
JsonNode类代表JSON树模型中的一个节点。它可以表达以下数据类型的JSON数据。Array, Binary, Boolean, Missing, Null, Number, Object, POJO, String. 这些数据类型在JsonNodeType enum中定义。
3. Getting Keys from JSON
3.从JSON中获取密钥
We’re using the following JSON as input in this article:
我们在本文中使用以下JSON作为输入。
{
"Name":"Craig",
"Age":10,
"BookInterests":[
{
"Book":"The Kite Runner",
"Author":"Khaled Hosseini"
},
{
"Book":"Harry Potter",
"Author":"J. K. Rowling"
}
],
"FoodInterests":{
"Breakfast":[
{
"Bread":"Whole wheat",
"Beverage":"Fruit juice"
},
{
"Sandwich":"Vegetable Sandwich",
"Beverage":"Coffee"
}
]
}
}
Here, we’re using a String object as input, but we can read JSON content from different sources such as File, byte[], URL, InputStream, JsonParser etc.
这里,我们使用一个String对象作为输入,但我们可以从不同的来源读取JSON内容,如File、byte[]、URL、InputStream、JsonParser等等。
Now, let’s discuss different approaches to fetch keys from a JSON.
现在,让我们来讨论从JSON中获取键的不同方法。
3.1. Using fieldNames
3.1.使用字段名
We can use fieldNames() method on a JsonNode instance to fetch the nested field names. It returns names of direct nested fields only.
我们可以在JsonNode实例上使用fieldNames()方法来获取嵌套字段名。它只返回直接嵌套字段的名称。
Let’s try it with a simple example:
让我们用一个简单的例子来试试。
public List<String> getKeysInJsonUsingJsonNodeFieldNames(String json, ObjectMapper mapper) throws JsonMappingException, JsonProcessingException {
List<String> keys = new ArrayList<>();
JsonNode jsonNode = mapper.readTree(json);
Iterator<String> iterator = jsonNode.fieldNames();
iterator.forEachRemaining(e -> keys.add(e));
return keys;
}
We get the following keys:
我们得到以下钥匙。
[Name, Age, BookInterests, FoodInterests]
In order to get all the inner nested nodes, we need to call fieldNames() method on nodes at each level:
为了获得所有的内部嵌套节点,我们需要在每一层的节点上调用fieldNames()方法。
public List<String> getAllKeysInJsonUsingJsonNodeFieldNames(String json, ObjectMapper mapper) throws JsonMappingException, JsonProcessingException {
List<String> keys = new ArrayList<>();
JsonNode jsonNode = mapper.readTree(json);
getAllKeysUsingJsonNodeFieldNames(jsonNode, keys);
return keys;
}
private void getAllKeysUsingJsonNodeFields(JsonNode jsonNode, List<String> keys) {
if (jsonNode.isObject()) {
Iterator<Entry<String, JsonNode>> fields = jsonNode.fields();
fields.forEachRemaining(field -> {
keys.add(field.getKey());
getAllKeysUsingJsonNodeFieldNames((JsonNode) field.getValue(), keys);
});
} else if (jsonNode.isArray()) {
ArrayNode arrayField = (ArrayNode) jsonNode;
arrayField.forEach(node -> {
getAllKeysUsingJsonNodeFieldNames(node, keys);
});
}
}
First, we check whether a JSON value is an object or array. If yes, we traverse the value object as well to fetch inner nodes.
As a result, we get all the key names present in JSON:
首先,我们检查一个JSON值是否是一个对象或数组。如果是,我们也会遍历该值对象以获取内部节点。
结果,我们得到JSON中存在的所有键名。
[Name, Age, BookInterests, Book, Author,
Book, Author, FoodInterests, Breakfast, Bread, Beverage, Sandwich, Beverage]
In the above example, we can also use fields() method of JsonNode class to get field objects instead of just field names:
在上面的例子中,我们也可以使用JsonNode类的fields()方法来获取字段对象,而不仅仅是字段名。
public List<String> getAllKeysInJsonUsingJsonNodeFields(String json, ObjectMapper mapper) throws JsonMappingException, JsonProcessingException {
List<String> keys = new ArrayList<>();
JsonNode jsonNode = mapper.readTree(json);
getAllKeysUsingJsonNodeFields(jsonNode, keys);
return keys;
}
private void getAllKeysUsingJsonNodeFields(JsonNode jsonNode, List<String> keys) {
if (jsonNode.isObject()) {
Iterator<Entry<String, JsonNode>> fields = jsonNode.fields();
fields.forEachRemaining(field -> {
keys.add(field.getKey());
getAllKeysUsingJsonNodeFieldNames((JsonNode) field.getValue(), keys);
});
} else if (jsonNode.isArray()) {
ArrayNode arrayField = (ArrayNode) jsonNode;
arrayField.forEach(node -> {
getAllKeysUsingJsonNodeFieldNames(node, keys);
});
}
}
3.2. Using JsonParser
3.2.使用JsonParser
We can also use the JsonParser class for low-level JSON parsing. JsonParser creates a sequence of iterable tokens from the given JSON content. Token types are specified as enums in JsonToken class as listed below:
我们还可以使用JsonParser类来进行低级别的JSON解析。JsonParser从给定的JSON内容中创建一个可迭代的令牌序列。令牌类型在JsonToken类中被指定为enums,如下所示。
- NOT_AVAILABLE
- START_OBJECT
- END_OBJECT
- START_ARRAY
- FIELD_NAME
- VALUE_EMBEDDED_OBJECT
- VALUE_STRING
- VALUE_NUMBER_INT
- VALUE_NUMBER_FLOAT
- VALUE_TRUE
- VALUE_FALSE
- VALUE_NULL
While iterating using JsonParser, we can check the token type and perform required operations. Let’s fetch all the field names for our example JSON string:
在使用JsonParser进行迭代时,我们可以检查标记类型并执行所需操作。让我们为我们的例子JSON字符串获取所有字段名。
public List<String> getKeysInJsonUsingJsonParser(String json, ObjectMapper mapper) throws IOException {
List<String> keys = new ArrayList<>();
JsonNode jsonNode = mapper.readTree(json);
JsonParser jsonParser = jsonNode.traverse();
while (!jsonParser.isClosed()) {
if (jsonParser.nextToken() == JsonToken.FIELD_NAME) {
keys.add((jsonParser.getCurrentName()));
}
}
return keys;
}
Here, we’ve used traverse() method of JsonNode class to get the JsonParser object. Similarly, we can create a JsonParser object using JsonFactory as well:
这里,我们使用了JsonNode类的traverse()方法来获取JsonParser对象。同样地,我们也可以使用JsonFactory创建一个JsonParser对象。
public List<String> getKeysInJsonUsingJsonParser(String json) throws JsonParseException, IOException {
List<String> keys = new ArrayList<>();
JsonFactory factory = new JsonFactory();
JsonParser jsonParser = factory.createParser(json);
while (!jsonParser.isClosed()) {
if (jsonParser.nextToken() == JsonToken.FIELD_NAME) {
keys.add((jsonParser.getCurrentName()));
}
}
return keys;
}
As a result, we get all the key names extracted from the example JSON content:
结果是,我们得到了从JSON内容的例子中提取的所有键名。
[Name, Age, BookInterests, Book, Author,
Book, Author, FoodInterests, Breakfast, Bread, Beverage, Sandwich, Beverage]
Notice, how concise the code is if we compare it to the other approaches we present in this tutorial.
请注意,如果我们将其与本教程中介绍的其他方法相比较,该代码是多么简洁。
3.3. Using Map
3.3.使用Map
We can use the readValue() method of ObjectMapper class to deserialize JSON content to a Map. Consequently, we can extract JSON elements while iterating over the Map object. Let’s try to fetch all keys from our example JSON using this approach:
我们可以使用ObjectMapper 类的readValue()方法来反序列化JSON内容到Map。因此,我们可以在遍历Map对象时提取JSON元素。让我们尝试用这种方法从我们的示例JSON中获取所有的键。
public List<String> getKeysInJsonUsingMaps(String json, ObjectMapper mapper) throws JsonMappingException, JsonProcessingException {
List<String> keys = new ArrayList<>();
Map<String, Object> jsonElements = mapper.readValue(json, new TypeReference<Map<String, Object>>() {
});
getAllKeys(jsonElements, keys);
return keys;
}
private void getAllKeys(Map<String, Object> jsonElements, List<String> keys) {
jsonElements.entrySet()
.forEach(entry -> {
keys.add(entry.getKey());
if (entry.getValue() instanceof Map) {
Map<String, Object> map = (Map<String, Object>) entry.getValue();
getAllKeys(map, keys);
} else if (entry.getValue() instanceof List) {
List<?> list = (List<?>) entry.getValue();
list.forEach(listEntry -> {
if (listEntry instanceof Map) {
Map<String, Object> map = (Map<String, Object>) listEntry;
getAllKeys(map, keys);
}
});
}
});
}
In this case also, after getting top-level nodes, we traverse the JSON objects that have values as either objects (maps) or arrays to get nested nodes.
在这种情况下,在得到顶层节点后,我们遍历JSON对象,这些对象的值要么是对象(地图),要么是数组,以得到嵌套节点。
4. Conclusion
4.总结
We’ve seen different ways to read key names from JSON content. Henceforth, we can extend the traversal logic discussed in the article to perform other operations on JSON elements as required.
我们已经看到了从JSON内容中读取键名的不同方法。从今以后,我们可以扩展文章中讨论的遍历逻辑,根据需要对JSON元素进行其他操作。
As always, the full source code of the examples can be found over on GitHub.
一如既往,可以在GitHub上找到这些例子的完整源代码。