在Jackson中使用树模型节点JsonNode
1.概述
使用JsonNode进行各种转换以及添加,修改和删除节点。
2.创建一个节点
创建节点的第一步是使用默认构造函数实例化ObjectMapper对象:
ObjectMapper mapper = new ObjectMapper();
由于创建ObjectMapper对象非常昂贵,因此建议将同一对象重复用于多个操作。
接下来,一旦有了ObjectMapper,就有三种不同的方式来创建树节点。
2.1 从头开始构建节点
创建节点的最常见方法如下:
JsonNode node = mapper.createObjectNode();
另外,也可以通过JsonNodeFactory创建一个节点:
JsonNode node = JsonNodeFactory.instance.objectNode();
2.2从JSON来源解析
之前文章《JSON字符串转换为JsonNode》
2.3从对象转换
可以通过在ObjectMapper上调用valueToTree(Object fromValue)方法来从Java对象转换节点:
JsonNode node = mapper.valueToTree(fromValue);
convertValue API在这里也很有帮助:
JsonNode node = mapper.convertValue(fromValue, JsonNode.class);
如何使用,创建NodeBean
@Data
@AllArgsConstructor
@NoArgsConstructor
public class NodeBean {private int id;private String name;
}
编写单元测试,确保转化正确
NodeBean fromValue = new NodeBean(2016, "123456.com");ObjectMapper mapper = new ObjectMapper();JsonNode node = mapper.valueToTree(fromValue);assertEquals(2016, node.get("id").intValue());assertEquals("123456.com", node.get("name").textValue());
3.转换节点
3.1写为JSON
将树节点转换为JSON字符串的基本方法如下:
mapper.writeValue(destination, node);
其中目标可以是File,OutputStream或Writer。
public class ExampleStructure {private static ObjectMapper mapper = new ObjectMapper();public static JsonNode getExampleRoot() throws IOException {return mapper.createObjectNode();}
}
String newString = "{\"nick\": \"cowtowncoder\"}";
ObjectMapper mapper = new ObjectMapper();
JsonNode newNode = mapper.readTree(newString);JsonNode rootNode = ExampleStructure.getExampleRoot();
((ObjectNode) rootNode).set("name", newNode);
//{"name":{"nick":"cowtowncoder"}}
System.out.println(rootNode.toString());assertFalse(rootNode.path("name").path("nick").isMissingNode());
assertEquals("cowtowncoder", rootNode.path("name").path("nick").textValue());
3.2转换为对象
将JsonNode转换为Java对象的最方便的方法是treeToValue 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);
示例:
ObjectMapper mapper = new ObjectMapper();
ObjectNode node = mapper.createObjectNode();
node.put("id", 2016);
node.put("name", "baeldung.com");NodeBean toValue = mapper.treeToValue(node, NodeBean.class);assertEquals(2016, toValue.getId());
assertEquals("baeldung.com", toValue.getName());
4.操纵树节点
json数据结构如下:
{"name": {"first": "Tatu","last": "Saloranta"},"title": "Jackson founder","company": "FasterXML"
}
4.1定位节点
在任何节点上工作之前,要做的第一件事是找到并将其分配给变量。
如果事先知道节点的路径,那将很容易做到。 例如,想要一个名为last的节点,该节点位于名称node下:
JsonNode locatedNode = rootNode.path("name").path("last");
String json = "{\"name\":{\"first\":\"Tatu\",\"last\":\"Saloranta\"},\"title\":\"Jackson founder\",\"company\":\"FasterXML\"}";ObjectMapper mapper = new ObjectMapper();JsonNode rootNode = mapper.readTree(json);JsonNode locatedNode = rootNode.path("name").path("last");System.out.println(locatedNode); //Saloranta
另外,也可以使用get或with API代替path。
4.2添加一个新节点
可以将一个节点添加为另一个节点的子节点,如下所示:
ObjectNode newNode = ((ObjectNode) locatedNode).put(fieldName, value);
put的许多重载变体可以用于添加不同值类型的新节点。
还可以使用许多其他类似方法,包括putArray,putObject,PutPOJO,putRawValue和putNull。
最后,让看一个示例,在该示例中,将整个结构添加到树的根节点:
"address":
{"city": "Seattle","state": "Washington","country": "United States"
}
String json = "{\"name\":{\"first\":\"Tatu\",\"last\":\"Saloranta\"},\"title\":\"Jackson founder\",\"company\":\"FasterXML\"}";ObjectMapper mapper = new ObjectMapper();JsonNode rootNode = mapper.readTree(json);ObjectNode addedNode = ((ObjectNode) rootNode).putObject("address");addedNode.put("city", "Seattle").put("state", "Washington").put("country", "United States");System.out.println(rootNode);//{"name":{"first":"Tatu","last":"Saloranta"},"title":"Jackson founder","company":"FasterXML","address":{"city":"Seattle","state":"Washington","country":"United States"}}
4.3编辑节点
可以通过调用set(String fieldName,JsonNode value)方法来修改ObjectNode实例:
JsonNode locatedNode = locatedNode.set(fieldName, value);
通过对相同类型的对象使用replace或setAll方法,可以得到类似的结果。
ObjectMapper mapper = new ObjectMapper();
String newString = "{\"nick\": \"cowtowncoder\"}";
JsonNode newNode = mapper.readTree(newString);JsonNode rootNode = mapper.createObjectNode();
((ObjectNode) rootNode).set("name", newNode);
System.out.println(rootNode);//{"name":{"nick":"cowtowncoder"}}
System.out.println(rootNode.path("name").path("nick").isMissingNode()); //false
System.out.println(rootNode.path("name").path("nick").textValue());//cowtowncoder
4.4移除一个节点
可以通过在父节点上调用remove(String fieldName)API来删除该节点:
JsonNode removedNode = locatedNode.remove(fieldName);
为了一次删除多个节点,可以使用Collection 类型的参数调用一个重载方法,该方法将返回父节点而不是要删除的父节点:
ObjectNode locatedNode = locatedNode.remove(fieldNames);
示例:
String json = "{\"name\":{\"first\":\"Tatu\",\"last\":\"Saloranta\"},\"title\":\"Jackson founder\",\"company\":\"FasterXML\"}";ObjectMapper mapper = new ObjectMapper();JsonNode rootNode = mapper.readTree(json);((ObjectNode) rootNode).remove("company");System.out.println(rootNode.path("company").isMissingNode());//trueSystem.out.println(rootNode); //{"name":{"first":"Tatu","last":"Saloranta"},"title":"Jackson founder"}
5.遍历节点
遍历JSON文档中的所有节点,并将它们重新格式化为YAML。 JSON具有三种类型的节点,分别是**Value,Object和Array**。
数据结构如下:
{"name": {"first": "Tatu","last": "Saloranta"}, //Object类型节点"title": "Jackson founder", //Value类型节点"company": "FasterXML", //Value类型节点"pets" : [ //Array类型节点{"type": "dog","number": 1},{"type": "fish","number": 50}]
}
生成的YAML:
name: first: Tatulast: Saloranta
title: Jackson founder
company: FasterXML
pets:
- type: dognumber: 1
- type: fishnumber: 50
JSON节点具有分层树结构。 因此,遍历整个JSON文档的最简单方法是从顶部开始,然后逐步向下遍历所有子节点。
将根节点传递给递归方法。 然后,该方法将使用提供的节点的每个子节点调用自身。
5.1测试遍历
创建一个简单的测试开始,该测试检查是否可以成功将JSON转换为YAML。
将JSON文档的根节点提供给的toYaml方法,输出转化结果:
@Testpublic void test15() throws IOException {String json = "{\"name\":{\"first\":\"Tatu\",\"last\":\"Saloranta\"},\"title\":\"Jackson founder\",\"company\":\"FasterXML\",\"pets\":[{\"type\":\"dog\",\"number\":1},{\"type\":\"fish\",\"number\":50}]}";ObjectMapper mapper = new ObjectMapper();JsonNode rootNode = mapper.readTree(json);JsonNodeIterator jsonNodeIterator = new JsonNodeIterator();String s = jsonNodeIterator.toYaml(rootNode);System.out.println(s);}
//JsonNodeIterator类
public String toYaml(JsonNode root) {StringBuilder yaml = new StringBuilder(); processNode(root, yaml, 0); return yaml.toString(); }
}
5.2处理不同的节点类型
需要稍微不同地处理不同类型的节点。 在processNode方法中执行此操作:
private void processNode(JsonNode jsonNode, StringBuilder yaml, int depth) {//是一个valueif (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方法即可获取该值的String表示形式。
接下来,看一下Array节点。 Array节点中的每个项目本身都是JsonNode,因此需要遍历Array并将每个节点传递给appendNodeToYaml方法。 还需要知道这些节点是数组的一部分。
但是,节点本身不包含任何告诉的内容,因此需要将一个标志传递给appendNodeToYaml方法。
最后,要遍历每个Object节点的所有子节点。 一种选择是使用JsonNode.elements。 但是,无法从元素中确定字段名称,因为它仅包含字段值,代码如下:
@Testpublic void test16() throws IOException {String json = "{\"name\":{\"first\":\"Tatu\",\"last\":\"Saloranta\"},\"title\":\"Jackson founder\",\"company\":\"FasterXML\",\"pets\":[{\"type\":\"dog\",\"number\":1},{\"type\":\"fish\",\"number\":50}]}";ObjectMapper mapper = new ObjectMapper();JsonNode rootNode = mapper.readTree(json);Iterator<JsonNode> elements = rootNode.elements();while(elements.hasNext()){JsonNode next = elements.next(); //紧输出值 不包含字段名称System.out.println(next);}}
输出结果如下:
Object {"first": "Tatu", "last": "Saloranta"}Value "Jackson Founder" Value "FasterXML" Array [{"type": "dog", "number": 1},{"type": "fish", "number": 50}]
不包含 Obejct Value Array
相反,可以使用JsonNode.fields,因为可以访问字段名称和值:
@Testpublic void test17() throws IOException {String json = "{\"name\":{\"first\":\"Tatu\",\"last\":\"Saloranta\"},\"title\":\"Jackson founder\",\"company\":\"FasterXML\",\"pets\":[{\"type\":\"dog\",\"number\":1},{\"type\":\"fish\",\"number\":50}]}";ObjectMapper mapper = new ObjectMapper();JsonNode rootNode = mapper.readTree(json);Iterator<Map.Entry<String, JsonNode>> fields = rootNode.fields();while(fields.hasNext()){Map.Entry<String, JsonNode> next = fields.next();System.out.println(next);}}
输出结果如下:
name={"first":"Tatu","last":"Saloranta"}
title="Jackson founder"
company="FasterXML"
pets=[{"type":"dog","number":1},{"type":"fish","number":50}]
对于每个字段,将字段名称添加到输出中。 然后将值传递给processNode方法,将其作为子节点处理:
private void appendNodeToYaml(JsonNode node, StringBuilder yaml, int depth, boolean isArrayItem) {//用于访问此JSON对象的所有字段(包含名称和值)的方法。Iterator<Map.Entry<String, JsonNode>> fields = node.fields();boolean isFirst = true;while (fields.hasNext()) {//获取 jsonNode 名称 和 值 key=valueMap.Entry<String, JsonNode> jsonField = fields.next();//jsonField.getKey() 获取 keyaddFieldNameToYaml(yaml, jsonField.getKey(), depth, isArrayItem && isFirst);//jsonField.getValue() 获取valueprocessNode(jsonField.getValue(), yaml, depth+1);isFirst = false;}}
无法从该节点知道它深度是多少。 因此,将一个称为depth的字段传递到processNode方法中,以对此进行跟踪。 每次获得子节点时,我们都会增加此值,以便可以正确缩进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输出,可以运行测试以表明其有效。
完成代码如下:
public class JsonNodeIterator {private static final String NEW_LINE = "\n";private static final String FIELD_DELIMITER = ": ";private static final String ARRAY_PREFIX = "- ";private static final String YAML_PREFIX = " ";public String toYaml(JsonNode root) {StringBuilder yaml = new StringBuilder();processNode(root, yaml, 0);return yaml.toString();}private void processNode(JsonNode jsonNode, StringBuilder yaml, int depth) {//是一个valueif (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);}}private void appendNodeToYaml(JsonNode node, StringBuilder yaml, int depth, boolean isArrayItem) {//用于访问此JSON对象的所有字段(包含名称和值)的方法。Iterator<Map.Entry<String, JsonNode>> fields = node.fields();boolean isFirst = true;while (fields.hasNext()) {//获取 jsonNode 名称 和 值 key=valueMap.Entry<String, JsonNode> jsonField = fields.next();//jsonField.getKey() 获取 keyaddFieldNameToYaml(yaml, jsonField.getKey(), depth, isArrayItem && isFirst);//jsonField.getValue() 获取valueprocessNode(jsonField.getValue(), yaml, depth+1);isFirst = false;}}private void addFieldNameToYaml(StringBuilder yaml, String fieldName, int depth, boolean isFirstInArray) {if (yaml.length()>0) {yaml.append(NEW_LINE);int requiredDepth = (isFirstInArray) ? depth-1 : depth;for(int i = 0; i < requiredDepth; i++) {yaml.append(YAML_PREFIX);}if (isFirstInArray) {yaml.append(ARRAY_PREFIX);}}yaml.append(fieldName);yaml.append(FIELD_DELIMITER);}}
在Jackson中使用树模型节点JsonNode相关推荐
- 在Jackson中使用树模型节点(JsonNode)详解
1. Overview 本文将重点介绍如何在Jackson中使用树模型节点. 我们将使用JsonNode进行各种转换以及添加.修改和删除节点. 2. 创建一个节点 创建节点的第一步是使用默认构造函数实 ...
- 一文带你入门机器学习中的树模型(附源码)
树模型 树模型在机器学习中至关重要,它不仅本身具有较好的性能,也可以用于优化其他的算法. 我们在本节将要介绍优化KNN~KNN~ KNN 算法的树模型以及决策树. 一.KNN~KNN~ KNN 的数据 ...
- 【机器学习】为什么在信用风险建模中首选树模型?
信用风险建模是一个可以使用机器学习来提供解决方案的领域,因为它能够从大量异构数据中找到答案.在信用风险建模中,还需要推断特征,因为它们在数据驱动的决策中非常重要. 在这篇文章中,我们来研究什么是信用风 ...
- 机器学习中的树模型下——集成类树
集成学习 集成学习顾名思义是通过构建并结合集成多个学习器来完成学习任务,有时也被称为多分类系统.集成学习要获得好的结果应做到"好而不同",即个体学习器要有一定的准确性,并且学习器之 ...
- 怎么在sketchup中隐藏树模型的枝叶边线
炸开模型...炸开模型,然后右击只选择边(需要安装SUAPP,网盘里有),然后隐藏,便可. 同理,其他的也一样,隐藏便可.
- 7. Jackson用树模型处理JSON是必备技能,不信你看
每棵大树,都曾只是一粒种子.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习.关注公众号[BA ...
- jackson中JSON字符串节点遍历和修改
有些场景下,在实现一些基础服务和拦截器的时候,我们可能需要在不知道JSON字符串所属对象类型的情况下,对JSON字符串中的某些属性进行遍历和修改,比如,设置或查询一些报文头字段. 在jackson中, ...
- 【机器学习】树模型预剪枝和后剪枝
在树模型建模的过程中的树模型的超参数会影响模型的精度,那么如何调整超参数呢?可以提前限制模型的超参数,也可以在训练模型之后再调整.本文将介绍树模型的预剪枝和后剪枝的实践过程. 原始模型 使用基础数据集 ...
- 树模型与线性模型的区别 决策树分类和逻辑回归分类的区别 【总结】
树模型与线性模型的区别在于: (一)树模型 ①树模型产生可视化的分类规则,可以通过图表表达简单直观,逐个特征进行处理,更加接近人的决策方式 ②产生的模型可以抽取规则易于理解,即解释性比线性模型强. ...
最新文章
- MicroPython技术及应用定义
- Hbase源码分析:Hbase UI中Requests Per Second的具体含义
- 《剑指offer》第1~11题:刷题week1[C++题解]
- Centos配置在线yum源
- 最新变体Transformer!∞-former!DeepMind 出品!
- (转)Scala中的Some和Option
- matlab 求导数
- acdsee怎么改图片大小|acdsee怎么用
- javascript 判断为负数_JavaScript判断数字正负数
- 字符串的解读和标签模板
- 将DOM对象转换成图片
- git在push时候出现timeout的解决方法
- 北京理工大学计算机实验广域网通信与有,北京理工大学计算机实验七报告表.doc...
- WDF开发USB设备驱动教程(2)
- CIO应当牢记iPhone的七条安全建议
- 韩国CMS集团用金属材质设计与表面处理技术引领全球市场
- 一成电计算机考研国家线2O 9,【九〇六 | 打卡】考研“国家线”只是起点,我们要挑战骇浪惊涛!...
- java榨汁机榨取不同水果,榨汁机别乱买,亲测榨水果翻车,九阳迷你原汁机评测...
- NCE4 L7 Bats
- Flask13:一对多关系实现
热门文章
- Windows电脑桌面云便签怎么关闭删除音效?
- [ERROR] Plugin org.apache.maven.plugins:maven-archetype-plugin:RELEASE or one of its dependencies...
- 快速近似计算Gamma函数以及函数阶乘
- 自媒体平台企鹅号怎样三天快速过试运营
- Object类九大方法之wait方法
- python美化excel_Python 使用 prettytable 库打印表格美化输出功能
- Echarts折线图之区域面积图
- 模块式浪涌保护器概述和防雷元件解析
- 【传感器大赏】3轴数字加速度传感器(1.5g)
- 各种跳转语句以及其适用场合+页面跳转在三种编码形式中的实现