Перейти к основному содержимому

Получить все ключи в строке JSON с помощью JsonNode

· 6 мин. чтения

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 .