【问题标题】:Flatten a JSON string to Map<String, String> using Gson or Jackson使用 Gson 或 Jackson 将 JSON 字符串展平为 Map<String, String>
【发布时间】:2015-04-20 22:20:56
【问题描述】:

例如:

{
    "id" : "123",
    "name" : "Tom",
    "class" : {
        "subject" : "Math",
        "teacher" : "Jack"
    }
}

我想得到Map&lt;String, String&gt;

"id" : "123",
"name" : "Tom",
"subject" : "Math",
"teacher" : "Jack"

【问题讨论】:

  • 您的问题是什么?这该怎么做?您提到了两个可以进行这种转换的库。 Stack Overflow 通常不适合问“哪个可比较的库更好”类型的问题。
  • 我不是在问哪个库更好。我实际上问过如何使用 Jackson 将 json 展平为 Map 的对象。如果杰克逊是不可能的,那么 Gson 呢?
  • @greg-449 问题不重复;它询问是否返回扁平地图。链接的问题返回一个递归地图,而不是一个展平的地图。
  • 我同意@m3th0dman。这个副本对回答这个问题没有用。投票决定重新开放。

标签: java json jackson gson


【解决方案1】:

我不确定是否存在开箱即用的东西(谈到 Gson)。但是,您可以编写自定义递归反序列化器:

Type t = new TypeToken<Map<String, String>>(){}.getType();
Gson gson = new GsonBuilder().registerTypeAdapter(t, new FlattenDeserializer()).create();
Map<String, String> map = gson.fromJson(new FileReader(new File("file")), t);

...

class FlattenDeserializer implements JsonDeserializer<Map<String, String>> {
    @Override
    public Map<String, String> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        Map<String, String> map = new LinkedHashMap<>();

        if (json.isJsonArray()) {
            for (JsonElement e : json.getAsJsonArray()) {
                map.putAll(deserialize(e, typeOfT, context));
            }
        } else if (json.isJsonObject()) {
            for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject().entrySet()) {
                if (entry.getValue().isJsonPrimitive()) {
                    map.put(entry.getKey(), entry.getValue().getAsString());
                } else {
                    map.putAll(deserialize(entry.getValue(), typeOfT, context));
                }
            }
        }
        return map;
    }
}

根据您的示例,它会输出:

{id="123", name="Tom", subject="Math", teacher="Jack"}

虽然它不能处理将键映射到基元数组的情况(因为无法在 Map&lt;String, String&gt; 中映射具有多个值的键(除非您可以采用数组的字符串表示或你可以返回一个Map&lt;String, Object&gt;),但它适用于一个对象数组(假设每个对象都有一个唯一的键)。

如果你有:

{
    "id": "123",
    "name": "Tom",
    "class": {
        "keys": [
            {
                "key1": "value1"
            },
            {
                "key2": "value2"
            }
        ],
        "teacher": "Jack"
    }
}

它会输出:

{id="123", name="Tom", key1="value1", key2="value2", teacher="Jack"}

如果需要处理更多案例,您可以根据需要自定义它。

希望对您有所帮助! :)

【讨论】:

  • 这就是我要找的。​​span>
【解决方案2】:

使用 ObjectMapper 将 Json 转换为 Java 类。以后有一个 Map 作为变量。

ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(new File("c:\\user.json"), User.class);

了解更多reference

【讨论】:

    【解决方案3】:

    使用杰克逊:

    private static <T> T fromJsonToGenericPojo(ObjectMapper objectMapper, 
                       String json, Class<?> classType, Class<?>... genericTypes)
    {
        JavaType javaType = TypeFactory.defaultInstance()
                        .constructParametricType(classType, genericTypes);
        try {
            return objectMapper.readValue(json, javaType);
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
            throw new IllegalStateException(e);
        }
    }
    

    你可以得到地图的地图:

    Map<String, Object> map = fromJsonToGenericPojo(objectMapper, json,    
                                     Map.class, String.class, Object.class);
    

    获得地图后,您可以根据需要使用 JDK 将其展平。没有通用的方法来展平地图。如果你有 JSON 怎么办:

    {
        "id" : "123",
        "name" : "Tom",
        "class" : [
        {
            "subject" : "Math",
            "teacher" : "Jack"
        }, 
        {
            "subject" : "English",
            "teacher" : "John"
        }]
    }
    

    关于数组中的元素,您将有冲突的键。你的决心是什么?只有最后一个值,只有第一个值,你会不会把键名从subject改成subject1 - "Math", subject2 - "English",不加一个吗?

    【讨论】:

      猜你喜欢
      • 2011-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-14
      • 2011-10-07
      相关资源
      最近更新 更多