使用Gson将json转换为Map.Entry对象

时间:2016-11-15 23:39:53

标签: java android json gson

EASY VERSION

如果我要求Gson将一些有效的json转换为MyMap,那么它就没有问题了

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


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

HARD VERSION:

如何让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}",则无关紧要

2 个答案:

答案 0 :(得分:4)

假设您有有效 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.Entry Deserializer,因为它不能开箱即用,因为它不是数组且{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);

答案 1 :(得分:1)

根据Map.Entry的文档:

  

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

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

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

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