Работа с узлами модели дерева в Джексоне
1. Обзор
Этот учебник будет сосредоточен на работе с узлы модели дерева в Джексоне .
Мы будем использовать JsonNode для различных конверсий, а также добавления, изменения и удаления узлов.
2. Создание узла
Первым шагом в создании узла является мгновенная ОбъектМаппер объект с помощью конструктора по умолчанию:
ObjectMapper mapper = new ObjectMapper();
С момента создания ОбъектМаппер объект стоит дорого, рекомендуется повторно использовать один и тот же объект для нескольких операций.
Далее, у нас есть три различных способа создать узел дерева, как только у нас есть ОбъектМаппер .
2.1. Построить узел из царапин
Наиболее распространенным способом создания узла из ничего является следующее:
JsonNode node = mapper.createObjectNode();
Кроме того, мы также можем создать узел через JsonNodeFactory :
JsonNode node = JsonNodeFactory.instance.objectNode();
2.2. Парс из источника JSON
Этот метод хорошо охвачены в Джексон – Маршалл Стринг JsonNode статьи. Пожалуйста, обратитесь к нему, если вам нужно больше информации.
2.3. Преобразование из объекта
Узел может быть преобразован из объекта Java, позвонив в valueToTree (объект отValue) метод на ОбъектМаппер :
JsonNode node = mapper.valueToTree(fromValue);
конвертироватьВалю API также полезен здесь:
JsonNode node = mapper.convertValue(fromValue, JsonNode.class);
Давайте посмотрим, как это работает на практике. Предположим, у нас есть класс под названием NodeBean :
public class NodeBean { private int id; private String name; public NodeBean() { } public NodeBean(int id, String name) { this.id = id; this.name = name; } // standard getters and setters }
Давайте напишем тест, который гарантирует, что преобразование происходит правильно:
@Test public void givenAnObject_whenConvertingIntoNode_thenCorrect() { NodeBean fromValue = new NodeBean(2016, "baeldung.com"); JsonNode node = mapper.valueToTree(fromValue); assertEquals(2016, node.get("id").intValue()); assertEquals("baeldung.com", node.get("name").textValue()); }
3. Преобразование узла
3.1. Выпишите как JSON
Основным методом преобразования узла дерева в строку JSON является следующее:
mapper.writeValue(destination, node);
где пункт назначения может быть Файл , ВыходСтрим или Писатель .
Повторное NodeBean
в разделе 2.3 тест гарантирует, что этот метод работает так, как ожидалось:
final String pathToTestFile = "node_to_json_test.json"; @Test public void givenANode_whenModifyingIt_thenCorrect() throws IOException { String newString = "{\"nick\": \"cowtowncoder\"}"; JsonNode newNode = mapper.readTree(newString); JsonNode rootNode = ExampleStructure.getExampleRoot(); ((ObjectNode) rootNode).set("name", newNode); assertFalse(rootNode.path("name").path("nick").isMissingNode()); assertEquals("cowtowncoder", rootNode.path("name").path("nick").textValue()); }
3.2. Преобразование в объект
Самый удобный способ преобразования JsonNode в Java-объект является деревоТоВалю API:
NodeBean toValue = mapper.treeToValue(node, NodeBean.class);
Что функционально эквивалентно:
NodeBean toValue = mapper.convertValue(node, NodeBean.class)
Мы также можем сделать это с помощью потока токенов:
JsonParser parser = mapper.treeAsTokens(node); NodeBean toValue = mapper.readValue(parser, NodeBean.class);
Наконец, давайте реализуем тест, который проверяет процесс преобразования:
@Test public void givenANode_whenConvertingIntoAnObject_thenCorrect() throws JsonProcessingException { JsonNode node = mapper.createObjectNode(); ((ObjectNode) node).put("id", 2016); ((ObjectNode) node).put("name", "baeldung.com"); NodeBean toValue = mapper.treeToValue(node, NodeBean.class); assertEquals(2016, toValue.getId()); assertEquals("baeldung.com", toValue.getName()); }
4. Манипулирование узлами деревьев
Следующие элементы JSON, содержащиеся в файле под названием example.json , используются в качестве базовой структуры для действий, обсуждаемых в этом разделе, которые должны быть приняты на:
{ "name": { "first": "Tatu", "last": "Saloranta" }, "title": "Jackson founder", "company": "FasterXML" }
Этот файл JSON, расположенный на classpath, разобраен на дерево модели:
public class ExampleStructure { private static ObjectMapper mapper = new ObjectMapper(); static JsonNode getExampleRoot() throws IOException { InputStream exampleInput = ExampleStructure.class.getClassLoader() .getResourceAsStream("example.json"); JsonNode rootNode = mapper.readTree(exampleInput); return rootNode; } }
Обратите внимание, что корень дерева будет использоваться при иллюстрации операций на узлах в следующих подразрядах.
4.1. Поиск узла
Прежде чем работать над каким-либо узел, первое, что нам нужно сделать, это найти и назначить его переменной.
Если путь к узелу известен заранее, это довольно легко сделать. Например, скажем, нам нужен узел под названием последний , который находится под имя узел:
JsonNode locatedNode = rootNode.path("name").path("last");
Кроме того, получить или с API также можно использовать вместо путь .
Если путь не известен, поиск, конечно, станет более сложным и итеративным.
Мы можем увидеть пример итерации по всем узлам в 5. Итерирование над узлами
4.2. Добавление нового узла
Узел может быть добавлен в качестве ребенка другого узла следующим образом:
ObjectNode newNode = ((ObjectNode) locatedNode).put(fieldName, value);
Многие перегруженные варианты положить могут использоваться для добавления новых узлов различных типов значений.
Многие другие аналогичные методы также доступны, в том числе положить Аррай , putObject , PutPOJO , putRawValue и putNull .
Наконец – давайте посмотрим на пример – где мы добавим всю структуру в корневой узел дерева:
"address": { "city": "Seattle", "state": "Washington", "country": "United States" }
Вот полный тест проходит через все эти операции и проверки результатов:
@Test public void givenANode_whenAddingIntoATree_thenCorrect() throws IOException { JsonNode rootNode = ExampleStructure.getExampleRoot(); ObjectNode addedNode = ((ObjectNode) rootNode).putObject("address"); addedNode .put("city", "Seattle") .put("state", "Washington") .put("country", "United States"); assertFalse(rootNode.path("address").isMissingNode()); assertEquals("Seattle", rootNode.path("address").path("city").textValue()); assertEquals("Washington", rootNode.path("address").path("state").textValue()); assertEquals( "United States", rootNode.path("address").path("country").textValue(); }
4.3. Редактирование узла
ОбъектНоде экземпляр может быть изменен путем вызова набор (String fieldName, значение JsonNode) метод:
JsonNode locatedNode = locatedNode.set(fieldName, value);
Аналогичные результаты могут быть достигнуты с помощью заменить или setAll методы на объектах одного типа.
Чтобы убедиться, что метод работает, как ожидалось, мы изменим значение поля имя под корневой узел от объекта первый и последний в другой, состоящий только из Ник поле в тесте:
@Test public void givenANode_whenModifyingIt_thenCorrect() throws IOException { String newString = "{\"nick\": \"cowtowncoder\"}"; JsonNode newNode = mapper.readTree(newString); JsonNode rootNode = ExampleStructure.getExampleRoot(); ((ObjectNode) rootNode).set("name", newNode); assertFalse(rootNode.path("name").path("nick").isMissingNode()); assertEquals("cowtowncoder", rootNode.path("name").path("nick").textValue()); }
4.4. Удаление узла
Узел можно удалить, позвонив в удалить (String fieldName) API на родительском узеле:
JsonNode removedNode = locatedNode.remove(fieldName);
Для того, чтобы удалить несколько узлов одновременно, мы можем вызвать перегруженный метод с параметром Тип
ObjectNode locatedNode = locatedNode.remove(fieldNames);
В крайнем случае, когда мы хотим удалить все субноды данного узла – удалитьВсе API пригодится.
Следующий тест будет сосредоточен на первом методе, упомянутом выше, который является наиболее распространенным сценарием:
@Test public void givenANode_whenRemovingFromATree_thenCorrect() throws IOException { JsonNode rootNode = ExampleStructure.getExampleRoot(); ((ObjectNode) rootNode).remove("company"); assertTrue(rootNode.path("company").isMissingNode()); }
5. Итерирование над узлами
Давайте итерировать все узлы в документе JSON и переформатировать их в YAML. JSON имеет три типа узла, которые являются значение, объект, и Array.
Итак, давайте обеспечим, чтобы наши выборочных данных имеет все три различных типа, добавив массив:
{ "name": { "first": "Tatu", "last": "Saloranta" }, "title": "Jackson founder", "company": "FasterXML", "pets" : [ { "type": "dog", "number": 1 }, { "type": "fish", "number": 50 } ] }
Теперь давайте посмотрим, YAML мы хотим производить:
name: first: Tatu last: Saloranta title: Jackson founder company: FasterXML pets: - type: dog number: 1 - type: fish number: 50
Мы знаем, что узлы JSON имеют иерархическую структуру дерева. Таким образом, самый простой способ итерировать в течение всего документа JSON заключается в том, чтобы начать с самого верха и работать наш путь вниз через все узлы ребенка.
Мы перейдем корневой узел в рекурсивный метод. Затем метод будет называть себя с каждым ребенком поставляемого узла.
5.1. Тестирование итерации
Мы начнем с создания простого теста, который проверяет, что мы можем успешно преобразовать JSON в YAML.
Наш тест поставляет корневой узел документа JSON в наш вЯмл метод и утверждает, что возвращенное значение является то, что мы ожидаем:
@Test public void givenANodeTree_whenIteratingSubNodes_thenWeFindExpected() throws IOException { JsonNode rootNode = ExampleStructure.getExampleRoot(); String yaml = onTest.toYaml(rootNode); assertEquals(expectedYaml, yaml); } public String toYaml(JsonNode root) { StringBuilder yaml = new StringBuilder(); processNode(root, yaml, 0); return yaml.toString(); } }
5.2. Обработка различных типов узла
Мы должны обрабатывать различные типы узла немного по-разному. Мы сделаем это в нашем процессНод метод:
private void processNode(JsonNode jsonNode, StringBuilder yaml, int depth) { if (jsonNode.isValueNode()) { yaml.append(jsonNode.asText()); } else if (jsonNode.isArray()) { for (JsonNode arrayItem : jsonNode) { appendNodeToYaml(arrayItem, yaml, depth, true); } } else if (jsonNode.isObject()) { appendNodeToYaml(jsonNode, yaml, depth, false); } }
Во-первых, рассмотрим узел значения. Мы просто называем asText метод узла, чтобы получить Струнные представление значения.
Далее рассмотрим узел Array. Каждый элемент в узлах Array сам по себе является JsonNode , поэтому мы итерировать над массивом и передать каждый узел приложениеНодеТоЯмл метод. Мы также должны знать, что эти узлы являются частью массива.
К сожалению, сам узел не содержит ничего, что говорит нам об этом, поэтому мы пересмотрим флаг в наш приложениеНодеТоЯмл метод.
Наконец, мы хотим итерировать все детские узлы каждого узла объекта. Одним из вариантов является использование JsonNode.elements . Однако мы не можем определить название поля по элементу, так как оно просто содержит значение поля:
Object {"first": "Tatu", "last": "Saloranta"} Value "Jackson Founder" Value "FasterXML" Array [{"type": "dog", "number": 1},{"type": "fish", "number": 50}]
Вместо этого, мы будем использовать JsonNode.fields так как это дает нам доступ как к названию поля, так и к значению:
Key="name", Value=Object {"first": "Tatu", "last": "Saloranta"} Key="title", Value=Value "Jackson Founder" Key="company", Value=Value "FasterXML" Key="pets", Value=Array [{"type": "dog", "number": 1},{"type": "fish", "number": 50}]
Для каждого поля мы добавляем название поля к выходу. Затем обработать значение как узел ребенка, передав его в процессНод метод:
private void appendNodeToYaml( JsonNode node, StringBuilder yaml, int depth, boolean isArrayItem) { Iterator> fields = node.fields(); boolean isFirst = true; while (fields.hasNext()) { Entry jsonField = fields.next(); addFieldNameToYaml(yaml, jsonField.getKey(), depth, isArrayItem && isFirst); processNode(jsonField.getValue(), yaml, depth+1); isFirst = false; } }
Мы не можем сказать по узлам, сколько у него предков. Таким образом, мы проходим поле, называемое глубиной в процессНод метод, чтобы отслеживать это. Мы увеличиваем это значение каждый раз, когда получаем узел ребенка, чтобы правильно определить поля в нашем выходе YAML:
private void addFieldNameToYaml( StringBuilder yaml, String fieldName, int depth, boolean isFirstInArray) { if (yaml.length()>0) { yaml.append("\n"); int requiredDepth = (isFirstInArray) ? depth-1 : depth; for(int i = 0; i < requiredDepth; i++) { yaml.append(" "); } if (isFirstInArray) { yaml.append("- "); } } yaml.append(fieldName); yaml.append(": "); }
Теперь, когда у нас есть весь код для итерации узлов и создания вывода YAML, мы можем запустить наш тест, чтобы показать, что он работает.
6. Заключение
Этот учебник охватывал общие API и сценарии работы с моделью дерева в Джексоне.
И, как всегда, реализация всех этих примеров и фрагментов кода можно найти в более на GitHub – это Maven основе проекта, поэтому она должна быть легко импортировать и работать, как она есть.