请记住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实例基本上是嵌套地图的地图:
{Port={@alias=diagnostics, Enabled=false, Type=DIAGNOSTIC, Number=10033, Protocol=JDWP, ExtendedProperties={Property={@name=suspend, $=n}}}}
虽然我需要使用“点符号”来展平平面地图,如上所述。
我宁愿不自己实现这个,虽然目前我没有看到任何其他方式......
答案 0 :(得分:34)
你可以这样做来遍历树,并跟踪你有多深,找出点符号属性名称:
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是任意的,所以你最终可能会在键名中发生冲突。
答案 1 :(得分:17)
如何使用json-flattener。 https://github.com/wnameless/json-flattener
BTW,我是这个lib的作者。
String flattenedJson = JsonFlattener.flatten(yourJson);
Map<String, Object> flattenedJsonMap = JsonFlattener.flattenAsMap(yourJson);
// Result:
{
"Port.@alias":"defaultHttp",
"Port.Enabled":"true",
"Port.Number":"10092",
"Port.Protocol":"http",
"Port.KeepAliveTimeout":"20000",
"Port.ThreadPool.@enabled":"false",
"Port.ThreadPool.Max":"150",
"Port.ThreadPool.ThreadPriority":"5",
"Port.ExtendedProperties.Property[0].@name":"connectionTimeout",
"Port.ExtendedProperties.Property[0].$":"20000"
}
答案 2 :(得分:7)
怎么样:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.google.gson.Gson;
/**
* NOT FOR CONCURENT USE
*/
@SuppressWarnings("unchecked")
public class JsonParser{
Gson gson=new Gson();
Map<String, String> flatmap = new HashMap<String, String>();
public Map<String, String> parse(String value) {
iterableCrawl("", null, (gson.fromJson(value, flatmap.getClass())).entrySet());
return flatmap;
}
private <T> void iterableCrawl(String prefix, String suffix, Iterable<T> iterable) {
int key = 0;
for (T t : iterable) {
if (suffix!=null)
crawl(t, prefix+(key++)+suffix);
else
crawl(((Entry<String, Object>) t).getValue(), prefix+((Entry<String, Object>) t).getKey());
}
}
private void crawl(Object object, String key) {
if (object instanceof ArrayList)
iterableCrawl(key+"[", "]", (ArrayList<Object>)object);
else if (object instanceof Map)
iterableCrawl(key+".", null, ((Map<String, Object>)object).entrySet());
else
flatmap.put(key, object.toString());
}
}
答案 3 :(得分:1)
您可以使用Typesafe Config Library实现类似的功能,如下例所示:
import com.typesafe.config.*;
import java.util.Map;
public class TypesafeConfigExample {
public static void main(String[] args) {
Config cfg = ConfigFactory.parseString(
" \"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" +
"}");
// each key has a similar form to what you need
for (Map.Entry<String, ConfigValue> e : cfg.entrySet()) {
System.out.println(e);
}
}
}
答案 4 :(得分:1)
Spring Integration会产生预期的结果。
默认情况下,它将shouldFlattenKeys
属性设置为true并生成平面贴图(不嵌套,值始终为简单类型)。当shouldFlattenKeys=false
生成嵌套地图时
ObjectToMapTransformer旨在用作集成流程的一部分,但以独立方式使用它是完全正确的。您需要使用转换输入的有效负载构造org.springframework.messaging.Message
。 transform
方法返回org.springframework.messaging.Message
对象,其有效负载为Map
import org.springframework.integration.transformer.ObjectToMapTransformer;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.GenericMessage;
Message message = new GenericMessage(value);
ObjectToMapTransformer transformer = new ObjectToMapTransformer();
transformer.setShouldFlattenKeys(true);
Map<String,Object> payload = (Map<String, Object>) transformer
.transform(message)
.getPayload();
旁注:将Spring Integration添加到类路径只是为了使用单个类可能有点过头了,但是你可以检查这个类的实现并自己编写类似的解决方案。嵌套地图由杰克逊(org.springframework.integration.support.json.JsonObjectMapper#fromJson(payload, Map.class)
)生成,然后mapis以递归方式进行,将所有值作为集合展平。
答案 5 :(得分:0)
如果您事先知道结构,则可以定义Java类并使用gson将JSON解析为该类的实例:
YourClass obj = gson.fromJson(json, YourClass.class);
如果没有,那么我不确定你要做什么。您显然无法动态定义类,因此使用点符号访问已解析的JSON是不可能的。
除非你想要这样的东西:
Map<String, String> parsed = magicParse(json);
parsed["Port.ThreadPool.max"]; // returns 150
如果是这样,那么遍历你的地图并制作一个“扁平”的地图似乎不是太大的问题。
还是别的什么?
答案 6 :(得分:0)
我也不得不在我的项目中解决一个类似的问题,并发现 springframework.vault 有一个方法 flatten() 来做同样的事情。下面是一个示例代码。
//Json string to Map<String, Object>
String data = "Your json as string"
final ObjectMapper mapper = new ObjectMapper();
final MapType type = mapper.getTypeFactory().constructMapType(
Map.class, String.class, Object.class);
final Map<String, Object> map = mapper.readValue(data, type);
//Using springframework.vault flatten method
Map<String, String> keyMap = JsonMapFlattener.flattenToStringMap(map);
//Input
{"key": {"nested": 1}, "another.key": ["one", "two"] }
//Output
key.nested=1
another.key[0]=one
another.key[1]=two
记得添加依赖
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
答案 7 :(得分:-1)
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import java.util.Iterator;
import java.util.Map;
public class JsonMapFlattener {
public static Map<String, Object> flatten(Map<String, ? extends Object> inputMap) {
return org.springframework.vault.support.JsonMapFlattener.flatten(inputMap);
}
public static JSONObject unflatten(Map<String, ? extends Object> inputMap) {
Map<String, Object> map = flatten(inputMap);
Iterator<String> it = map.keySet().iterator();
JSONObject jo2 = new JSONObject();
while (it.hasNext()) {
String key = it.next();
Object value = map.get(key);
JSONPath.set(jo2, key, value);
}
return jo2;
}
}