如何将 JSON 反序列化为扁平的类似 Map 的结构? [英] How to deserialize JSON into flat, Map-like structure?

查看:35
本文介绍了如何将 JSON 反序列化为扁平的类似 Map 的结构?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请记住,JSON 结构是事先未知的,即它是完全任意的,我们只知道它是 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.

例如

以下 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 的结构,具有类似的键(为简洁起见,不包括以上所有内容):

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);

然而,生成的 Map 实例基本上是嵌套 Map 的 Map:

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 = "{
" +
      "   "Port":
" +
      "   {
" +
      "       "@alias": "defaultHttp",
" +
      "       "Enabled": "true",
" +
      "       "Number": "10092",
" +
      "       "Protocol": "http",
" +
      "       "KeepAliveTimeout": "20000",
" +
      "       "ThreadPool":
" +
      "       {
" +
      "           "@enabled": "false",
" +
      "           "Max": "150",
" +
      "           "ThreadPriority": "5"
" +
      "       },
" +
      "       "ExtendedProperties":
" +
      "       {
" +
      "           "Property":
" +
      "           [                         
" +
      "               {
" +
      "                   "@name": "connectionTimeout",
" +
      "                   "$": "20000"
" +
      "               }
" +
      "           ]
" +
      "       }
" +
      "   }
" +
      "}";

  @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 是任意的,因此最终可能会在键名称中发生冲突.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆