小编这两天的工作是把某个Json文件做格式转换,提取其中有用的信息。所以需要解析原Json文件,获取其Json结点和属性值,再拼装到另一个Json文件并输出。
一、解析Json文件
0. 简介
想要生成JsonNode对象都要先生成ObjectMapper类对象,它是产生Json结点的工厂。
ObjectNode是对象结点,可以存入对象属性。如 { "name":"Lilei", "age":"18", "school":"HNU" }
ArrayNode是数组结点,用于存放JsonNode数据。如[ "Lilei", "XiaoHong", {"nm1": "Jay", "nm2": "Kong"} ]
同时属性值和数组值都可以嵌套对象或数据,并且JsonNode是ObjectNode和ArrayNode的父接口。
1. 读取文件
读取文件时,传入文件输入流,文件对象或者文本内容都可以,支持灵活处理。
ObjectMapper mapper= new ObjectMapper();
JsonNode rootNode = mapper.readValue(stringName, JsonNode.class); /*方法1*/
JsonNode rootNode = mapper.readTree(data); /*方法2*/
2. 获取属性或子节点
以下向大家展示了如何获取结点,或某个域(field)对应的值(value),以及遍历不清楚子节点是对象结点还是数组结点时的访问方法。
要注意的是,获取子节点有path和get两种方法,他们在功能上几乎相同。但区别如下:
path:路径访问安全,当访问的结点不存在时返回“MissingNode”,我将其理解为没有任何属性值的结点,也可以通过asText()转换为String类型,值为空字串;当访问的属性无效时同样返回空字串""。
优势是不知道多层子节点的值是否一定存在时可以连续使用path,若不存在一律返回"";但不能判断结点是否存在。
get:路径访问不安全,其访问结点路径或属性无效时返回null,故使用连续使用get访问时,若路径不存在会出现空指针异常。
优势是可以用是否为null判断结点是否存在。
访问技巧:不知道节点路径上是否所有节点都存在,可以使用
JsonNode temp = xxxNode.path("prop1").path("prop2").....get("propN");
if (null != temp) { do something... } 这样的方式安全得到不一定存在的节点
//获取结点
JsonNode jsonNode = rootNode.path("属性名"); /*通过属性名获取子节点,且路径访问安全*/
JsonNode jsonNode = rootNode.path("属性名1").path("属性名2").path("属性名3"); /*可连续使用*/
JsonNode jsonNode = rootNode.get("属性名"); /*通过属性名获取子节点*/
//获取属性
String jsonData = jsonNode.path("属性名").asText(); /*获取该属性的value值*/
String jsonData = jsonNode.path("属性名").asInt();
... /*以下同理*/
//遍历数组结点
for (JsonNode jsonNode: NodeArray) {
...
}
//在不知道某个结点是对象结点还是数组结点时的遍历方法
//其中JsonNode是对象结点ObjectNode和数组结点ArrayNode的父接口
if (jsonNode instansof ArrayNode) {
for (JsonNode jsonNode: NodeArray) {
...
} else { /*else if (jsonNode instansof ObjectNode)*/
...
}
二、生成Json文件
1. 生成ObjectNode和ArrayNode
很多时候我们需要自己将信息拼装,得到目的json字串,所以对于结点的生成和属性注入也需要掌握
ObjectMapper mapper = new ObjectMapper();
//生成对象结点
ObjectNode objNode = mapper.createObjectNode();
objNode.put("属性名", 属性值); /*在jdk1.8中,简单值用put设置*/
objNode.set("属性名", 子节点); /*在jdk1.8中,子节点用set设置*/
//生成数组结点
ArrayNode arrNode = mapper.createArrayNode();
arrNode.add("属性或子节点"); /*数组结点添加元素不做简单值和结点类的区分*/
2. 将json写入文件
OutputStream outputStream= new FileOutputStream(new File("path"));
objectMapper.writeValue(outputStream, data);
三、实例讲解(你也可以不读题设直接看后文示例代码)
假设有Json数据如下,我们的任务是:将SentenceGroup中Sentence的content字段抽取,并重新拼装。不过Sentence的属性值可能是ObjectNode, 也可能是ArrayNode。
"SentenceGroup": {
"id": 12345,
"book": RenaiEnglish,
"Sentence": {
"id": 12346,
"updater": Mario,
"content": "I like apple"
}
}
"SentenceGroup": {
"id": 12345,
"book": RenaiEnglish,
"Sentence": [
{
"id": 12346,
"updater": Mario,
"content": "I like orange"
},
{
"id": 12347,
"updater": Kate,
"content": "I like banana"
}
]
}
示例代码:
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(data); /*设跟根节点是SentenceGroup*/
JsonNode resultNode = null;
JsonNode sentences = rootNode.path("Sentence");
if (sentence instanceof ArrayNode) {
resultNode = mapper.createArrayNode(); /*创建ArrayNode对象*/
for (JsonNode jsonNode: sentences) {
ObjectNode sentNode = mapper.createObjectNode(); /*声明新结点*/
String content = jsonNode.path("content").asText(); /*获取属性值*/
sentNode.put("content", content) /*存储属性值,存储结点得用set*/
//给ArrayNode添加元素
//操作1结果是 [{"content":"I like orange"}, {"content":"I like banana"}]
((ArrayNode) resultNode).add(sentNode);
//操作2结果是 ["I like orange", "I like banana"],少一层对象结点的嵌套
((ArrayNode) resultNode).add(content);
}
} else {
resultNode = mapper.createObjectNode(); /*创建ObjectNode对象*/
((ObjectNode)resultNode).put("content", content)
}
//输出resultNode略
//...