3

これは限定的な例であり、無意味ですが、問題を示しています。

次のコード:

import java.util.*;
import com.google.gson.*;

class X {}

class SomeType {
    private Map <X,  String> map;
    public SomeType() {
        this.map = new HashMap<X, String>();
        map.put(new X(), "b");
    }
}

public class FooMain {

    private static Gson gson = new GsonBuilder().serializeNulls().create();

    public static void main(String args[]) throws Exception {
        String foo = gson.toJson(new SomeType(), SomeType.class);
        System.out.println(foo);                           // line 20
        SomeType st = gson.fromJson(foo, SomeType.class);  // line 21
    }
}

次のエラーで失敗します:

 [java] Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 20
 [java]     at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:176)
 [java]     at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:40)
 [java]     at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:187)
 [java]     at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:146)
 [java]     at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:93)
 [java]     at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:172)
 [java]     at com.google.gson.Gson.fromJson(Gson.java:795)
 [java]     at com.google.gson.Gson.fromJson(Gson.java:761)
 [java]     at com.google.gson.Gson.fromJson(Gson.java:710)
 [java]     at com.google.gson.Gson.fromJson(Gson.java:682)
 [java]     at FooMain.main(FooMain.java:21)
 [java] Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 20
 [java]     at com.google.gson.stream.JsonReader.expect(JsonReader.java:339)
 [java]     at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:322)
 [java]     at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:165)
 [java]     ... 10 more

一方、20行目は次のように出力します:

{"map":{"X@185b10b":"b"}}
4

2 に答える 2

2

GsonMapキーを JSON キーとして使用し、マップ要素の値を値として使用して、をシリアル化します。クラスにはカスタムのオーバーライドされたメソッドXがないため、それを使用してシリアル化しますが、逆シリアル化することはできません。.を提供したとしても、実際には逆シリアル化できません。toString()Object#toString()X@185b10btoString()

すべてを正しくシリアル化できないエッジ ケースを見つけたと思います。JSON オブジェクトのキーはString.

于 2013-10-10T00:55:22.673 に答える
0

わかりましたので、Sotirios Delimanolis の回答のおかげで、次のことがわかりました。

  • Mapキークラスに対してのみカスタムデシリアライザーを提供できます。その場合、toStringそのキークラスのメソッドは「シリアル化」形式を提供する必要があります

    また

  • Mapクラス自体の汎用アダプター (シリアライザー + デシリアライザー) と、Mapキー クラスの汎用アダプター (シリアライザー + デシリアライザー) を提供できます。このメソッドの唯一の利点はtoString、キー クラスのメソッドを他の目的に使用でき、デシリアライザーと連携する必要がないことです。

以下は、2 番目のケースの完全なコードです。

import java.util.*;
import com.google.gson.*;
import java.lang.reflect.Type;

import org.apache.commons.lang3.StringUtils;

class X {
    public int x;
    public X(int x) {
        this.x = x;
    }
    public String toString() {
        return String.format("boo ha ha %d", x);
    }
}

class SomeType {
    private Map <X,  String> map;
    public SomeType() {
        this.map = new HashMap<X, String>();
        map.put(new X(2), "b");
    }
    public String toString() {
        List<String> rv = new ArrayList<>();
        for (X x : map.keySet())
            rv.add(String.format("%s -> %s\n", x.toString(), map.get(x)));
        return StringUtils.join(rv, "\n");
    }
}

public class FooMain {

    public static void main(String args[]) throws Exception {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.registerTypeAdapter(X.class, new XAdapter());
        gsonBuilder.registerTypeAdapter(Map.class, new MapAdapter());
        Gson gson = gsonBuilder.serializeNulls().create();
        SomeType original = new SomeType();
        System.out.println("original is: "+original);
        String foo = gson.toJson(original, SomeType.class);
        System.out.println("JSON form is: "+foo);                     
        SomeType reconstructed = gson.fromJson(foo, SomeType.class);  
        System.out.println("reconstructed is: "+reconstructed);
    }
}


class MapAdapter implements JsonSerializer<Map<?, ?>>, JsonDeserializer<Map<?, ?>> {

    @Override
    public JsonElement serialize(Map<?, ?> m, Type typeOfT, JsonSerializationContext context) {
        JsonArray rv = new JsonArray();
        for (Object k : m.keySet()) {
            JsonObject kv = new JsonObject();
            kv.add        ("k"     , context.serialize(k));
            kv.addProperty("ktype" , k.getClass().getName());
            kv.add        ("v"     , context.serialize(m.get(k)));
            kv.addProperty("vtype" , m.get(k).getClass().getName());
            rv.add(kv);
        }
        return rv;
    }

    @Override
    public Map<?, ?> deserialize(JsonElement _json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        JsonArray json = (JsonArray) _json;
        Map<Object, Object> rv = new HashMap<>();
        for (int i = 0 ; i < json.size() ; i++) {
            JsonObject o = (JsonObject) json.get(i);
            String ktype = o.getAsJsonPrimitive("ktype").getAsString();
            String vtype = o.getAsJsonPrimitive("vtype").getAsString();
            Class<?> kklass = null;
            Class<?> vklass = null;
            try {
                kklass = Class.forName(ktype);
                vklass = Class.forName(vtype);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                throw new JsonParseException(e.getMessage());
            }
            Object k = context.deserialize( o.get("k"), kklass);
            Object v = context.deserialize( o.get("v"), vklass);
            rv.put(k, v);
        }
        return rv;
    }

}


class XAdapter implements JsonSerializer<X>, JsonDeserializer<X> {

    @Override
    public JsonElement serialize(X x, Type typeOfT, JsonSerializationContext context) {
        return context.serialize(x.x);
    }

    @Override
    public X deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        String s = json.getAsString();
        int x = Integer.valueOf(s);
        return new X(x);
    }
}
于 2013-10-10T14:18:48.827 に答える