【问题标题】:Convert json to Map.Entry object with Gson使用 Gson 将 json 转换为 Map.Entry 对象
【发布时间】:2016-11-15 23:39:53
【问题描述】:

简易版

如果我要求 Gson 将一些有效的 json 转换为 MyMap,这样做没有问题

public class MyMap{
   Map<Long,String> content;
}


MyMap myMap = gson.fromJson(json, new TypeToken<MyMap>() {}.getType());

硬版:

如何让 Gson 执行以下操作?

public class MyDS{
    Map<Map.Entry<Long,String>,Map<Long,String>> content;
}

MyDS myDS = gson.fromJson(json, new TypeToken<MyDS>() {}.getType());

如果您确实需要,请提供示例 json。

"content": {
      "[1, dog]": {
        "1": "max",
        "2": "pi",
        "3": "robot",
        "4": "catcher",
        "5": "reaper"
      },
      "[2, cat]": {
        "6": "black",
        "7": "white",
        "8": "meow",
        "9": "mice",
        "10": "rat"
      },
      "[3, rabbit]": {
        "16": "bunny",
        "17": "ears",
        "28": "burgerbun",
        "39": "alice",
        "50": "tweak"
      }
    }

更多笔记

为了更好地衡量,我尝试运行一个单元测试,我所做的只是尝试使用 Gson 读取 json,然后我得到以下错误跟踪:

at sun.misc.Unsafe.allocateInstance(Native method)
java.lang.reflect.Method.invoke!(Native method)
com.google.gson.internal.UnsafeAllocator$1.newInstance(UnsafeAllocator.java:48)
com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:223)
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:207)
com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:40)
com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:186)
com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:145)
com.google.gson.Gson.fromJson(Gson.java:861)
com.google.gson.Gson.fromJson(Gson.java:826)
com.google.gson.Gson.fromJson(Gson.java:775)

对于"{3, rabbit}",密钥的格式是否为"[3, rabbit]" 无关紧要

【问题讨论】:

  • 你能展示一下这个示例 json 吗?
  • 引号只是这样,因为我粘贴到文本编辑器中。但他们很好。就像我说的,它一直有效,直到我需要一个条目作为密钥。

标签: java android json gson


【解决方案1】:

假设您有 有效 JSON 类型的内容:

{
   "content": {
      "[1, dog]": {
        "1": "max",
        "2": "pi",
        "3": "robot",
        "4": "catcher",
        "5": "reaper"
      },
      "[2, cat]": {
        "6": "black",
        "7": "white",
        "8": "meow",
        "9": "mice",
        "10": "rat"
      },
      "[3, rabbit]": {
        "16": "bunny",
        "17": "ears",
        "28": "burgerbun",
        "39": "alice",
        "50": "tweak"
      }
   }
}

要实现你想要的,你可以简单地实现你自己的Map.EntryDeserializer,因为它不能开箱即用地反序列化,因为它不是一个数组并且{3, rabbit}不是一个有效的JSON对象。

因此您的Deserializer 可以依靠正则表达式 来提取键和值,然后使用提取的值创建AbstractMap.SimpleEntry 的实例,例如:

public class MapEntryDeserializer implements JsonDeserializer<Map.Entry<Long, String>> {

    /**
     * Pattern corresponding to:
     * Starts with [
     * <a non empty sequence of digit characters>,
     * <a non empty sequence of any characters
     * Ends with ]
     */
    private static final Pattern PATTERN = Pattern.compile("^\\[(\\d+), ?(.+)\\]$");

    public Map.Entry<Long, String> deserialize(JsonElement json, Type typeOfT, 
        JsonDeserializationContext context) throws JsonParseException {
        // Extract the key/value pair from Strings of type [3, rabbit]
        String value = json.getAsString();
        Matcher matcher = PATTERN.matcher(value);
        if (!matcher.find()) {
            throw new JsonParseException(
                String.format("The map entry doesn't have the expected format: %s", value)
            );
        }
        return new AbstractMap.SimpleEntry<>(
            Long.valueOf(matcher.group(1)), matcher.group(2)
        );
    }
}

然后我可以反序列化我的 JSON 内容:

Type type = new TypeToken<MyDS>() {}.getType();
Gson gson = new GsonBuilder()
    .registerTypeAdapter(Map.Entry.class, new MapEntryDeserializer())
    .create();

MyDS myDS = gson.fromJson(json, type);

【讨论】:

  • 很好的解决方案,我没有得到第二组正则表达式([^]]+)你能解释一下吗?
  • @AxelH 表示除]以外的任何字符的序列
  • 我想我们快到了。但我不断收到以下错误:"The map entry doesn't have the expected format: 1=dog
  • 非常感谢。我使用value.split("[ ,=]")。 +1,检查和赏金。非常感谢。
  • @NouvelTravay 如果您愿意,您确实可以使用 split 但也可以提取正则表达式以获得更好的性能
【解决方案2】:

根据 Map.Entry 的文档:

获取对映射条目的引用的唯一方法是从迭代器 这个集合视图。

https://docs.oracle.com/javase/8/docs/api/java/util/Map.Entry.html

这意味着您在创建初始地图之前无法获取 Map.Entry。为了实现您想要的,您需要将 JSON 解析为 Map,然后对其进行迭代以将其插入到您的 MyDS 对象中。

话虽如此,根据您的最终用途,可能会有更好的方法在解析数据后重新组织/键入数据。

【讨论】:

  • 文档指的是地图中条目的常规用法,但实际上并没有阻止您创建Map.Entry 的实例,甚至还有诸如AbstractMap.SimpleEntry 和@987654324 之类的实用程序实现@.
猜你喜欢
  • 1970-01-01
  • 2011-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-15
  • 2015-03-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多