如何将JSON反序列化为平面,类似Map的结构? [英] How to deserialize JSON into flat, Map-like structure?
问题描述
请记住JSON结构之前是未知的,即它完全是任意的,我们只知道它是JSON格式。
例如,> p>
以下JSON
{
端口:
{
@alias:defaultHttp,
已启用:true,
数字:10092,
协议: http,
KeepAliveTimeout:20000,
ThreadPool:
{
@enabled:false,
Max: 150,
ThreadPriority:5
},
ExtendedProperties:
{
物业:
[
{
@ name:connectionTimeout,
$:20000
}
]
}
}
}
应该反序列化为类似Map的结构,其中包含键(不是全部)以上简称包括:
port [0] .alias
port [0] .enabled
port [0] .extendedProperties.connectionTimeout
port [0] .threadPool.max
我目前正在调查杰克逊,所以我们有:
TypeReference< HashMap< String,Object>> typeRef = new TypeReference< HashMap< String,Object>>(){};
Map< String,String> o = objectMapper.readValue(jsonString,typeRef);
但是,生成的Map实例基本上是嵌套地图的地图:
{Port = {@ alias = diagnostics,Enabled = false,Type = DIAGNOSTIC,Number = 10033,Protocol = JDWP,ExtendedProperties = {Property = {@ name =暂停,$ = n}}}}
虽然我需要使用点平展键的平面地图符号,就像上面那样。
我宁愿不自己实现这个,虽然目前我没有看到任何其他方式......
您可以执行此操作来遍历树并跟踪您找出点符号属性名称的深度:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ValueNode;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.junit.Test;
公共类FlattenJson {
String json ={\ n+
\Port \:\ n+
{ \ n+
\@ alias \:\defaultHttp \,\ n+
\Enabled \:\true \\ \\,\ n+
\Number \:\10092 \,\ n+
\Protocol \:\ http:\\,\ n+
\KeepAliveTimeout \:\20000 \,\ n+
\ThreadPool \:\\ \\ n+
{\ n+
\@ enabled\:\false \,\ n+
\ Max \:\150 \,\ n+
\ThreadPriority \:\5 \\ n+
}, \ n+
\ExtendedProperties \:\ n+
{\ n+
\ \\:\ n+
[\ n+
{\ n+ +
\@ name\:\connectionTimeout \ ,\ n+
\$ \:\20000 \\ n+
} \ n+
] \\ \\ n+
} \ n+
} \ n+
};
@Test
public void testCreatingKeyValues(){
Map< String,String> map = new HashMap< String,String>();
try {
addKeys(,new ObjectMapper()。readTree(json),map);
} catch(IOException e){
e.printStackTrace();
}
System.out.println(map);
}
private void addKeys(String currentPath,JsonNode jsonNode,Map< String,String> map){
if(jsonNode.isObject()){
ObjectNode objectNode =(ObjectNode)jsonNode;
Iterator< Map.Entry< String,JsonNode>> iter = objectNode.fields();
String pathPrefix = currentPath.isEmpty()? :currentPath +。;
while(iter.hasNext()){
Map.Entry< String,JsonNode> entry = iter.next();
addKeys(pathPrefix + entry.getKey(),entry.getValue(),map);
}
} else if(jsonNode.isArray()){
ArrayNode arrayNode =(ArrayNode)jsonNode;
for(int i = 0; i< arrayNode.size(); i ++){
addKeys(currentPath +[+ i +],arrayNode.get(i),map) ;
}
} else if(jsonNode.isValueNode()){
ValueNode valueNode =(ValueNode)jsonNode;
map.put(currentPath,valueNode.asText());
}
}
}
它生成以下地图:
Port.ThreadPool.Max = 150,
Port.ThreadPool。@ enabled = false,
Port .Number = 10092,
Port.ExtendedProperties.Property [0]。@ name = connectionTimeout,
Port.ThreadPool.ThreadPriority = 5,
Port.Protocol = http,
Port .KeepAliveTimeout = 20000,
Port.ExtendedProperties.Property [0]。$ = 20000,
Port。@ alias = defaultHttp,
Port.Enabled = true
它应该很容易剥离 @
和 $
在属性名称中,尽管你可能最终会在键名中发生冲突,因为你说JSON是任意的。
Have in mind that the JSON structure is not known before hand i.e. it is completely arbitrary, we only know that it is JSON format.
For example,
The following JSON
{
"Port":
{
"@alias": "defaultHttp",
"Enabled": "true",
"Number": "10092",
"Protocol": "http",
"KeepAliveTimeout": "20000",
"ThreadPool":
{
"@enabled": "false",
"Max": "150",
"ThreadPriority": "5"
},
"ExtendedProperties":
{
"Property":
[
{
"@name": "connectionTimeout",
"$": "20000"
}
]
}
}
}
Should be deserialized into Map-like structure having keys like (not all of the above included for brevity):
port[0].alias
port[0].enabled
port[0].extendedProperties.connectionTimeout
port[0].threadPool.max
I am looking into Jackson currently, so there we have:
TypeReference<HashMap<String, Object>> typeRef = new TypeReference<HashMap<String, Object>>() {};
Map<String, String> o = objectMapper.readValue(jsonString, typeRef);
However, the resulting Map instance is basically a Map of nested Maps:
{Port={@alias=diagnostics, Enabled=false, Type=DIAGNOSTIC, Number=10033, Protocol=JDWP, ExtendedProperties={Property={@name=suspend, $=n}}}}
While I need flat Map with flatten keys using "dot notation", like the above.
I would rather not implement this myself, although at the moment I don't see any other way...
You can do this to traverse the tree and keep track of how deep you are to figure out dot notation property names:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ValueNode;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.junit.Test;
public class FlattenJson {
String json = "{\n" +
" \"Port\":\n" +
" {\n" +
" \"@alias\": \"defaultHttp\",\n" +
" \"Enabled\": \"true\",\n" +
" \"Number\": \"10092\",\n" +
" \"Protocol\": \"http\",\n" +
" \"KeepAliveTimeout\": \"20000\",\n" +
" \"ThreadPool\":\n" +
" {\n" +
" \"@enabled\": \"false\",\n" +
" \"Max\": \"150\",\n" +
" \"ThreadPriority\": \"5\"\n" +
" },\n" +
" \"ExtendedProperties\":\n" +
" {\n" +
" \"Property\":\n" +
" [ \n" +
" {\n" +
" \"@name\": \"connectionTimeout\",\n" +
" \"$\": \"20000\"\n" +
" }\n" +
" ]\n" +
" }\n" +
" }\n" +
"}";
@Test
public void testCreatingKeyValues() {
Map<String, String> map = new HashMap<String, String>();
try {
addKeys("", new ObjectMapper().readTree(json), map);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(map);
}
private void addKeys(String currentPath, JsonNode jsonNode, Map<String, String> map) {
if (jsonNode.isObject()) {
ObjectNode objectNode = (ObjectNode) jsonNode;
Iterator<Map.Entry<String, JsonNode>> iter = objectNode.fields();
String pathPrefix = currentPath.isEmpty() ? "" : currentPath + ".";
while (iter.hasNext()) {
Map.Entry<String, JsonNode> entry = iter.next();
addKeys(pathPrefix + entry.getKey(), entry.getValue(), map);
}
} else if (jsonNode.isArray()) {
ArrayNode arrayNode = (ArrayNode) jsonNode;
for (int i = 0; i < arrayNode.size(); i++) {
addKeys(currentPath + "[" + i + "]", arrayNode.get(i), map);
}
} else if (jsonNode.isValueNode()) {
ValueNode valueNode = (ValueNode) jsonNode;
map.put(currentPath, valueNode.asText());
}
}
}
It produces the following map:
Port.ThreadPool.Max=150,
Port.ThreadPool.@enabled=false,
Port.Number=10092,
Port.ExtendedProperties.Property[0].@name=connectionTimeout,
Port.ThreadPool.ThreadPriority=5,
Port.Protocol=http,
Port.KeepAliveTimeout=20000,
Port.ExtendedProperties.Property[0].$=20000,
Port.@alias=defaultHttp,
Port.Enabled=true
It should be easy enough to strip out @
and $
in the property names, although you could end up with collisions in key names since you said the JSON was arbitrary.
这篇关于如何将JSON反序列化为平面,类似Map的结构?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!