请记住,JSON 结构是事先不知道的,即它是完全任意的,我们只知道它是 JSON 格式。
例如,
以下 JSON
{
"Port":
{
"@alias": "defaultHttp",
"Enabled": "true",
"Number": "10092",
"Protocol": "http",
"KeepAliveTimeout": "20000",
"ThreadPool":
{
"@enabled": "false",
"Max": "150",
"ThreadPriority": "5"
},
"ExtendedProperties":
{
"Property":
[
{
"@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 实例基本上是嵌套 Map 的 Map:
{Port={@alias=diagnostics, Enabled=false, Type=DIAGNOSTIC, Number=10033, Protocol=JDWP, ExtendedProperties={Property={@name=suspend, $=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; 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()); } } }
它生成以下地图:
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 是任意的,因此最终可能会在键名称中发生冲突。