使用Gson反序列化映射键需要一个对象

4 java serialization json gson deserialization

我收到错误:

Exception in thread "main" com.google.gson.JsonParseException: 
Expecting object found: "com.shagie.app.SimpleMap$Data@24a37368"
Run Code Online (Sandbox Code Playgroud)

尝试解除使用非平凡键的Map时:

package com.shagie.app;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.util.HashMap;

public class SimpleMap {
    public static void main(String[] args) {
        Wrapper w = new Wrapper();
        w.m.put(new Data("f", 1), new Data("foo", 3));
        w.m.put(new Data("b", 2), new Data("bar", 4));

        GsonBuilder gb = new GsonBuilder();
        gb.setPrettyPrinting();
        Gson g = gb.create();

        String json = g.toJson(w);

        System.out.println(json);

        w = g.fromJson(json, Wrapper.class);
        System.out.println(w.m.isEmpty());
    }

    static public class Wrapper {
        HashMap<Data, Data> m = new HashMap<Data, Data>();
    }

    static public class Data {
        String s;
        Integer i;
        public Data(String arg, Integer val) { s = arg; i = val; }
    }
}
Run Code Online (Sandbox Code Playgroud)

这序列化为json:

{
  "m": {
    "com.shagie.app.SimpleMap$Data@24a37368": {
      "s": "foo",
      "i": 3
    },
    "com.shagie.app.SimpleMap$Data@66edc3a2": {
      "s": "bar",
      "i": 4
    }
  }
}

可以看到尝试序列化的密钥,但肯定不是可以反序列化的.

如何序列化此对象以便可以反序列化?

dad*_*son 10

我在尝试解决这个难题时发现了以下内容:问题210:无法使用复杂键序列化或反序列化地图.

对于未来的任何互联网旅行者(如我自己)...您可以使用GsonBuilder上的enableComplexMapKeySerialization()方法在GSON 2.*中启用此功能.

这是该方法javadoc.

启用后,映射将作为[key,value]数组的数组序列化(并正确反序列化):

{"m":[[{"s":"f", "i",1}, {"s":"foo", "i":3}], [{"s":"b", "i",2}, {"s":"bar", "i":4}]]}
Run Code Online (Sandbox Code Playgroud)


小智 4

问题是toString()在映射的键上调用,而不是它们本身被序列化。

要解决此问题,需要设置自定义序列化器和反序列化器,并且反序列化器需要了解对象用于将自身显示为字符串的格式(该toString()方法必须返回可用于重建整个对象的字符串) )。

对于上面的例子:

package com.shagie.app;

import com.google.gson.*;

import java.lang.reflect.Type;
import java.util.HashMap;

public class SimpleMapFixed {
    public static void main(String[] args) {
        Wrapper w = new Wrapper();
        w.m.put(new Data("f", 1), new Data("foo", 3));
        w.m.put(new Data("b", 2), new Data("bar", 4));

        GsonBuilder gb = new GsonBuilder();
        gb.setPrettyPrinting();
        gb.registerTypeAdapter(Data.class, new DataSerializer());
        Gson g = gb.create();

        String json = g.toJson(w);
        System.out.println(json);

        w = g.fromJson(json, Wrapper.class);
        System.out.println(w.m.isEmpty());
    }

    static public class Wrapper {
        HashMap<Data, Data> m = new HashMap<Data, Data>();
    }

    static public class DataSerializer implements JsonSerializer<Data>,
                                                  JsonDeserializer<Data> {
        @Override
        public Data deserialize(JsonElement je, Type t, JsonDeserializationContext ctx)
                throws JsonParseException {
            Data rv;
            JsonObject jo;

            System.out.println("deserialize called with: " + je.toString());

            if (je.isJsonObject()) {
                jo = je.getAsJsonObject();
                rv = new Data(jo.get("s").getAsString(), jo.get("i").getAsInt());
            } else {
                String js = je.getAsString();
                String[] s = js.split(":", 2);  // split into two (and only two)
                rv = new Data(s[1], Integer.valueOf(s[0]));
            }

            System.out.println("deserialize returns: " + rv.s + " " + rv.i);
            return rv;
        }

        @Override
        public JsonElement serialize(Data data, Type type, JsonSerializationContext jsonSerializationContext) {
            JsonObject jo = new JsonObject();
            jo.addProperty("s", data.s);
            jo.addProperty("i", data.i);
            System.out.println("serialize called: " + jo.toString());
            return jo;
        }
    }

    static public class Data {
        String s;
        Integer i;

        public Data(String arg, Integer val) { s = arg; i = val; }

        @Override
        public String toString() {
            String rv = i.toString() + ':' + s;
            System.out.println("toString called: " + rv);
            return rv;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

运行此代码会产生:

序列化调用:{"s":"foo","i":3}
toString 调用:1:f
序列化调用:{"s":"bar","i":4}
toString 调用:2:b
{
  “米”:{
    “1:f”:{
      “s”:“富”,
      “我”:3
    },
    “2:b”:{
      “s”:“酒吧”,
      “我”:4
    }
  }
}
反序列化调用:“1:f”
反序列化返回:f 1
反序列化调用:{"s":"foo","i":3}
反序列化返回: foo 3
反序列化调用:“2:b”
反序列化返回:b 2
反序列化调用:{"s":"bar","i":4}
反序列化返回:栏 4

toString()请注意作为序列化一部分的调用。在此代码中,从 String 形式反序列化的逻辑位于 中DataSerializer,尽管将其作为另一个构造函数移动到类中可能是有意义的Data- 它不会影响最终结果。

进一步注意,这Data是一个相当简单的对象本身,没有更深层次的结构。尝试将其序列化为密钥需要额外的工作。