1. Обзор
В этой статье мы рассмотрим различные способы извлечения всех вложенных ключей из JSON с помощью JsonNode
. Мы стремимся пройти через строку JSON и собрать имена ключей в списке.
2. Введение
Библиотека Джексона использует древовидную модель для представления данных JSON . Модель дерева предоставляет нам эффективный способ взаимодействия с иерархическими данными.
Объекты JSON представлены в виде узлов в модели дерева . Это упрощает выполнение операций CRUD с содержимым JSON.
2.1. ObjectMapper
Мы используем методы класса ObjectMapper
для чтения содержимого JSON. Метод ObjectMapper.readTree()
десериализует JSON и строит дерево экземпляров JsonNode
. Он принимает источник JSON в качестве входных данных и возвращает корневой узел созданной модели дерева. Впоследствии мы можем использовать корневой узел для обхода всего дерева JSON.
Древовидная модель не ограничивается только чтением обычных объектов Java. Существует однозначное соответствие между полями JSON и моделью дерева. Таким образом, каждый объект, будь то POJO или нет, может быть представлен как узел.
Следовательно, нам нравится гибкий подход к представлению содержимого JSON в виде общих узлов .
Чтобы узнать больше, обратитесь к нашей статье о Jackson ObjectMapper.
2.2. JsonNode
Класс JsonNode
представляет узел в модели дерева JSON. Он может выражать данные JSON в следующих типах данных: Array, Binary, Boolean, Missing, Null, Number, Object, POJO, String.
Эти типы данных определены в перечислении
JsonNodeType
. ``
3. Получение ключей из JSON
Мы используем следующий 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"
}
]
}
}
Здесь мы используем объект String
в качестве входных данных, но мы можем читать содержимое JSON из разных источников, таких как File
, byte[]
, URL
, InputStream
, JsonParser
и т. д.
Теперь давайте обсудим различные подходы к извлечению ключей из JSON.
3.1. Использование имен полей
Мы можем использовать метод fieldNames() для экземпляра
JsonNode
, чтобы получить имена вложенных полей. Он возвращает имена только прямых вложенных полей .
Давайте попробуем это на простом примере:
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;
}
Получаем следующие ключи:
[Name, Age, BookInterests, FoodInterests]
Чтобы получить все внутренние вложенные узлы, нам нужно вызвать метод 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);
});
}
}
Сначала мы проверяем, является ли значение JSON объектом или массивом. Если да, мы также обходим объект значения, чтобы получить внутренние узлы.
В результате получаем все имена ключей, присутствующие в JSON:
[Name, Age, BookInterests, Book, Author,
Book, Author, FoodInterests, Breakfast, Bread, Beverage, Sandwich, Beverage]
В приведенном выше примере мы также можем использовать метод fields() класса
JsonNode
для получения объектов полей, а не только имен полей:
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. Использование JsonParser
Мы также можем использовать класс JsonParser
для низкоуровневого разбора JSON . JsonParser
создает последовательность итерируемых токенов из заданного содержимого JSON. Типы токенов указываются как перечисления
в классе JsonToken
, как указано ниже:
НЕТ В НАЛИЧИИ
START_OBJECT
END_OBJECT
START_ARRAY
ПОЛЕ_ИМЯ
VALUE_EMBEDDED_OBJECT
VALUE_STRING
VALUE_NUMBER_INT
VALUE_NUMBER_FLOAT
VALUE_TRUE
VALUE_FALSE
VALUE_NULL
Во время итерации с помощью 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;
}
Здесь мы использовали метод traverse() класса
JsonNode
для получения объекта JsonParser
. Точно так же мы можем создать объект JsonParser
, используя JsonFactory
:
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;
}
В результате мы получаем все имена ключей, извлеченные из содержимого примера JSON:
[Name, Age, BookInterests, Book, Author,
Book, Author, FoodInterests, Breakfast, Bread, Beverage, Sandwich, Beverage]
Обратите внимание, насколько лаконичен код, если сравнить его с другими подходами, которые мы представляем в этом руководстве.
3.3. Использование карты
Мы можем использовать метод readValue () класса
ObjectMapper
для десериализации содержимого JSON в Map
. Следовательно, мы можем извлекать элементы JSON при переборе объекта Map .
Давайте попробуем получить все ключи из нашего примера 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);
}
});
}
});
}
В этом случае также, после получения узлов верхнего уровня, мы просматриваем объекты JSON, которые имеют значения либо в виде объектов (карт), либо в виде массивов, чтобы получить вложенные узлы.
4. Вывод
Мы видели разные способы чтения имен ключей из содержимого JSON. Отныне мы можем расширить логику обхода, обсуждаемую в статье, для выполнения других операций с элементами JSON по мере необходимости.
Как всегда, полный исходный код примеров можно найти на GitHub .