杰克逊前

开始使用Spring 5和Spring Boot 2,通过学习的春天课程:

>>看看这个课程

1.概述

本教程将重点介绍如何使用Jackson树模型节点

我们将使用JsonNode用于各种转换以及添加、修改和删除节点。

2.创建一个节点

创建节点的第一步是实例化objectmap通过使用默认构造函数:

ObjectMapper = new ObjectMapper();

自从创建objectmap对象开销很大,建议在多个操作中重用同一个对象。

接下来,我们有三种不同的方法来创建树节点objectmap

2.1。从头构造一个节点

最常见的无中生有创建节点的方法如下:

JsonNode node = mapper.createObjectNode();

属性也可以创建节点JsonNodeFactory:

JsonNode node = JsonNodeFactory.instance.objectNode();

2.2。从JSON源解析

方法中详细介绍了此方法Jackson - Marshall字符串到JsonNode篇文章。如果您需要更多的信息,请参考它。

2.3。从对象转换

类可以将节点从Java对象转换为fromValue valueToTree(对象)方法objectmap:

JsonNode node = mapper.valueToTree(fromValue);

convertValueAPI在这里也很有用:

JsonNode node = mapper。convertValue(fromValue, JsonNode.class);

让我们看看它在实践中是如何工作的。假设我们有一个名为NodeBean:

public class NodeBean{私有int id;私人字符串名称;public NodeBean() {} public NodeBean(int id, String name) {this。id = id;this.name =名称;} //标准的getter和setter方法}

让我们编写一个测试来确保转换正确进行:

@Test public void givenanobject_whenconvertingintonode_threct () {NodeBean fromValue = new NodeBean(2016,“baeldu金宝搏188体育ng.com”);JsonNode node = mapper.valueToTree(fromValue);assertequal(2016年,node.get (" id ") .intValue ());assertequal ("金宝搏188体育 baeldung.com " node.get(“名字”).textValue ());}

3.把一个节点

3.1。写出JSON格式

将树节点转换为JSON字符串的基本方法如下:

映射器。writeValue(目的地节点);

目的地可以是哪里文件,一个OutputStream或者一个作家
通过重用类NodeBean在2.3节中声明了一个测试,确保这个方法按照预期工作:

最终字符串pathToTestFile = "node_to_json_test.json";@Test public void givenanode_whenmodifyingit_thcorrect () throws IOException {String newString = "{\"nick\": \"cowtowncoder\"}";JsonNode newNode = mapper.readTree(newString);JsonNode rootNode = ExampleStructure.getExampleRoot();((ObjectNode) rootNode)。设置(“名字”,newNode);assertFalse (rootNode.path(“名字”).path(尼克).isMissingNode ());rootNode.path assertequal(“cowtowncoder”(“名字”).path(尼克).textValue ());}

3.2。转换为对象

转换a最方便的方法JsonNodetreeToValueAPI:

NodeBean toValue = mapper。treeToValue(node, NodeBean.class);

在功能上相当于:

NodeBean toValue = mapper。convertValue(node, NodeBean.class)

我们也可以通过令牌流来做到这一点:

JsonParser解析器= mapper.treeAsTokens(节点);NodeBean toValue = mapper。readValue(解析器,NodeBean.class);

最后,让我们实现一个测试来验证转换过程:

@Test public void givenanode_whenconvertinginto anobject_threct () throws JsonProcessingException {JsonNode node = mapper.createObjectNode();((ObjectNode)节点)。put (" id ", 2016);((ObjectNode)节点)。把("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创始人","company": "FasterXML"}

这个JSON文件位于类路径中,被解析为一个模型树:

public class ExampleStructure {private static ObjectMapper = new ObjectMapper();静态JsonNode getexamplepleroot()抛出IOException {InputStream exampleInput = ExampleStructure.class.getClassLoader() .getResourceAsStream("example.json");JsonNode rootNode = mapper.readTree(exampleInput);返回rootNode;}}

请注意,在下面的子部分中说明对节点的操作时将使用树的根。

4.1。定位一个节点

在处理任何节点之前,我们需要做的第一件事是定位它并将其赋值给一个变量。

如果节点的路径事先已知,那么这很容易做到。例如,假设我们想要一个命名的节点去年,它在的名字节点:

JsonNode locatedNode = rootNode.path(“name”).path(“last”);

或者,得到api也可以用来代替路径

如果路径是未知的,搜索当然会变得更加复杂和迭代。

我们可以看到一个遍历中所有节点的示例5.遍历节点

4.2。添加新节点

一个节点可以作为另一个节点的子节点添加,如下所示:

ObjectNode newNode = ((ObjectNode) locatedNode)。把(字段名、价值);

的许多重载变体可用于添加不同值类型的新节点。

许多其他类似的方法也可用,包括putArray,propertynames,PutPOJO,putRawValueputNull

最后——让我们看一个例子——我们在树的根节点上添加了一个完整的结构:

地址:{"城市":"西雅图","州":"华盛顿","国家":"美国"}

下面是通过所有这些操作并验证结果的完整测试:

@Test public void givenanode_whenaddingintoatree_threct () throws IOException {JsonNode rootNode = ExampleStructure.getExampleRoot();ObjectNode addedNode = ((ObjectNode) rootNode).putObject("address");add node .put(“城市”,“西雅图”).put(“州”,“华盛顿”).put(“国家”,“美国”);assertFalse (rootNode.path(“地址”).isMissingNode ());assertequal(“西雅图”,rootNode.path .path(“地址”)(“城市”).textValue ());assertequal(“华盛顿”,rootNode.path .path(“地址”)(“状态”).textValue ());assertEquals(“美国”,rootNode.path(“地址”).path(“国家”).textValue();}

4.3。编辑一个节点

一个ObjectNode实例可以通过调用来修改set(String fieldName, JsonNode value)方法:

JsonNode locatedNode = locatedNode。集(字段名,值);

类似的结果可以通过使用取代setAll方法在相同类型的对象上。

为了验证方法是否如预期的那样工作,我们将更改字段的值的名字的对象下的根节点第一个去年变成另一个只有尼克测试中的字段:

@Test public void givenanode_whenmodifyingit_thcorrect () throws IOException {String newString = "{\"nick\": \"cowtowncoder\"}";JsonNode newNode = mapper.readTree(newString);JsonNode rootNode = ExampleStructure.getExampleRoot();((ObjectNode) rootNode)。设置(“名字”,newNode);assertFalse (rootNode.path(“名字”).path(尼克).isMissingNode ());rootNode.path assertequal(“cowtowncoder”(“名字”).path(尼克).textValue ());}

4.4。删除一个节点

调用删除(String字段名)API的父节点:

JsonNode removedNode = locatedNode.remove(fieldName);

为了一次性删除多个节点,可以调用参数为的重载方法收集<字符串>类型,它返回父节点而不是要删除的节点:

ObjectNode locatedNode = locatedNode.remove(fieldNames);

在极端情况下,当我们想删除给定节点的所有子节点- - - - - -removeAllAPI迟早会派上用场。

下面的测试将集中在上面提到的第一种方法——这是最常见的场景:

@Test public void givenanode_whenremovingfromatree_thorrect () throws IOException {JsonNode rootNode = ExampleStructure.getExampleRoot();((ObjectNode) rootNode) .remove(“公司”);assertTrue (rootNode.path(“公司”).isMissingNode ());}

5.遍历节点

让我们遍历JSON文档中的所有节点,并将它们重新格式化为YAML。JSON有三种类型的节点,分别是值、对象和数组。

因此,让我们通过添加数组:

{" name ":{“第一”:“一本正经”,“最后一次”:“Saloranta”},“标题”:“杰克逊创始人”,“公司”:“FasterXML”、“宠物”:[{“类型”:“狗”,“数量”:1},{“类型”:“鱼”,“数量”:50}]}

现在,让我们看看我们想要生产的YAML:

name: first: Tatu last: Saloranta title: Jackson创始人company: FasterXML宠物:- type:狗数量:1 - type:鱼数量:50

我们知道JSON节点有一个层次树结构。因此,遍历整个JSON文档的最简单方法是从顶部开始,然后遍历所有子节点。

我们将把根节点传递给递归方法。然后,该方法将使用所提供节点的每个子节点调用自身。

5.1。测试迭代

我们将首先创建一个简单的测试,检查是否可以成功地将JSON转换为YAML。

我们的测试将JSON文档的根节点提供给toYaml方法并断言返回值是我们所期望的:

@Test public void givenanodetree_wheniteratingsubnodes_thenwefinexpected() throws IOException {JsonNode rootNode = ExampleStructure.getExampleRoot();String yaml = onTest.toYaml(rootNode);assertequal (expectedYaml yaml);} public String toYaml(JsonNode root) {StringBuilder yaml = new StringBuilder();processNode(根、yaml、0);返回yaml.toString ();}}

5.2。不同节点类型的处理

我们需要以略微不同的方式处理不同类型的节点。我们会在processNode方法:

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);}}

首先,让我们考虑Value节点。我们简单地称之为asText方法得到字符串值的表示。

接下来,让我们看看Array节点。Array节点中的每一项本身就是一个JsonNode,因此我们遍历Array并将每个节点传递给appendNodeToYaml方法。我们还需要知道这些节点是数组的一部分。

不幸的是,节点本身不包含任何告诉我们这一点的内容,因此我们将向appendNodeToYaml方法。

最后,我们想遍历每个Object节点的所有子节点。一种选择是使用JsonNode.elements。然而,我们不能从元素中确定字段名,因为它只包含字段值:

对象{"first": "Tatu", "last": "Saloranta"} Value "Jackson Founder" Value " fastxml " 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}]

对于每个字段,我们将字段名添加到输出中。然后将该值作为子节点传递给processNode方法:

private void appendNodeToYaml(JsonNode node, StringBuilder yaml, int depth, boolean isArrayItem) {Iterator> fields = node.fields();boolean isFirst = true;while (fields.hasNext()){条目 jsonField = fields.next();addFieldNameToYaml(yaml, jsonField.getKey(), depth, isArrayItem && isFirst);yaml processNode (jsonField.getValue(),深度+ 1);isFirst = false;}}

我们不能从节点中知道它有多少祖先。我们传递一个深度字段到processNode方法来跟踪这一点。每当我们得到一个子节点时,我们就增加这个值,以便我们可以正确地缩进YAML输出中的字段:

private void addFieldNameToYaml(StringBuilder yaml, String fieldName, int depth, boolean isFirstInArray) {if (yaml.length()>0) {yaml.append("\n");int requiredDepth = (isfirstarray) ?(报告:深度;For (int I = 0;我< requiredDepth;我+ +){yaml。追加(" ");} if (isfirstarray) {yaml.;追加(“-”);}} yaml.append(字段名);yaml。append(": "); }

现在我们已经有了遍历节点并创建YAML输出的所有代码,我们可以运行我们的测试来证明它是有效的。

6.结论

本教程介绍了在Jackson中使用树模型的常见api和场景。

和往常一样,所有这些示例和代码片段的实现都可以在在GitHub-这是一个基于maven的项目,所以它应该很容易导入和运行。

杰克逊底部

开始使用Spring 5和Spring Boot 2,通过学习的春天课程:

>>看看这个课程
2评论
最古老的
最新的
内联反馈
查看所有评论
对这篇文章的评论关闭!