在gson java中反序列化集合的困惑

时间:2014-01-06 11:52:09

标签: java gson

我对gson反序列化中的集合有疑问

结果:
第1行:编译没有任何错误。
第2行:它编译没有任何错误。打印[1.0,2.0,3.0,4.0]。
第2行:它抛出“线程中的异常”main“java.lang.ClassCastException:java.lang.Double无法强制转换为gsonUserGuide.com.google.sites.A”

我的问题: 我知道我可以正确指定类型如下。

ArrayList<Integer> aList = gson.fromJson( 
            "[1,2,3,4]", new TypeToken<ArrayList<Integer>>() {}.getType());

但我的问题是,如果它在第3行中抛出java.lang.ClassCastException,为什么它至少在运行时将值加载到aList时不会出错?

代码:

public class OBJConversion {

    private final static Gson gson = new Gson();

    public static void main(String[] args) {

        // Line 1
        ArrayList<A> aList = gson.fromJson("[1,2,3,4]", ArrayList.class);
        // Line 2
        System.out.println("main().aList : " + aList);
        // Line 3
        System.out.println("main().someString : " + aList.get(0).getSomeString());
    }

    class A {

        String someString = null;

        public String getSomeString() {
            return someString;
        }

        public void setSomeString(String someString) {
            this.someString = someString;
        }
    }
}

2 个答案:

答案 0 :(得分:2)

这是因为类型信息(<A>中的ArrayList<A>)在运行时不再可用。通用类型在编译期间专门使用,并且被删除&#34;之后,代码最终被编译成类似

的东西
ArrayList aList = gson.fromJson("[1,2,3,4]", ArrayList.class);

(注意删除声明中的<A>类型参数)这就是为什么你必须在运行时提供类型信息(通过&#34;类型标记&#34;),如果你想让代码真的安全。类型信息仅在特殊情况下可用(实际上由类型标记实现使用)。

答案 1 :(得分:2)

为什么

使用GSON时,这是Collections Limitations之一:

  

您可以序列化任意对象的集合,但可以反序列化,因为用户无法指示生成的对象的类型。

GSON将gson.fromJson("[1,2,3,4]", ArrayList.class)解释为基元列表(它无法从ArrayList<A>告诉您需要类型A,因为类型信息在运行时丢失了)。 GSON具有different primitives而不是Java,并且没有int,因此[1,2,3,4]被视为具有双浮点精度的数字列表,即Java Double。因此,GSON有效地创建了List<Double>

解决方法

GSON建议three workarounds

  1. 手工解析,如this example
  2. Collection.class
  3. 注册类型适配器
  4. A注册类型适配器并使用fromJson(aList, Collection<A>.class)
  5. 例如,选项2看起来像这样:

    public class OBJConversion {
    
        public static void main(String[] args) {
    
            Gson gson = new GsonBuilder()
                    // NOTE: all Lists will be treated as List<A>
                    .registerTypeAdapter(List.class, new ACollectionAdapter())
                    .create();
    
            // Line 1
            List<A> aList = gson.fromJson("[1,2,3,4]", List.class));
            // Line 2
            System.out.println("main().aList : " + aList);
            // Line 3
            System.out.println("main().someString : " + aList.get(0).getSomeString());
        }
    
        static class ACollectionAdapter extends TypeAdapter<List<A>> {
    
            @Override
            public void write(
                    JsonWriter out, List<A> value) throws IOException {
                out.beginArray();
                for (A a : value) {
                    out.value(a.getSomeString());
                }
                out.endArray();
            }
    
            @Override
            public List<A> read(JsonReader in) throws IOException {
                List<A> as = new ArrayList<A>();
                in.beginArray();
                while (in.hasNext()) {
                    A a = new A();
                    a.setSomeString(in.nextString());
                    as.add(a);
                }
                in.endArray();
                return as;
            }
        }
    
        static class A {
    
            String someString = null;
    
            public String getSomeString() {
                return someString;
            }
    
            public void setSomeString(String someString) {
                this.someString = someString;
            }
        }
    }