2011-05-10 91 views
13

我在GSON中遇到了一些奇怪的行爲。序列化接口列表GSON

如果我有下面的類結構:

public interface Animal { 
    public void nothing(); 
} 

public class Cat implements Animal { 
    private String name; 

    public Cat(String name) { 
     super(); 
     this.name = name; 
    } 

    public Cat(){} 

    @Override 
     public void nothing() { 
     // TODO Auto-generated method stub 
     }; 
    } 

public class Dog implements Animal { 
    private String name; 

    public Dog(String name) { 
      super(); 
     this.name = name; 
    } 

    public Dog(){} 

    @Override 
    public void nothing() { 
     // TODO Auto-generated method stub 

    }; 
} 

我可以這樣做:

ArrayList<Animal> animals = new ArrayList<Animal>(); 
    animals.add(new Cat("Betty")); 
    animals.add(new Dog("Fred")); 
    System.out.println(gson.toJson(animals)); 

,並得到如下的輸出:

[{"name":"Betty"},{"name":"Fred"}] 

但是,如果我把animals成一個包含類別:

public class Container { 

List<Animal> animals = new ArrayList<Animal>(); 

public void addAnimal(Animal a){ 
    animals.add(a); 
} 
} 

,並呼籲:

Container container = new Container(); 
container.addAnimal(new Cat("betty")); 
System.out.println(gson.toJson(container)); 

我得到:當該名單是由本身

{"animals":[{}]} 

它看起來像GSON可序列化的接口List<Interface>的名單,但是當被包含在列表在另一課上,GSON有問題。

任何想法我做錯了什麼?

作爲一個方面說明,我可以正確地反序列化JSON字符串使用自定義解串器正確的類型。這是序列化,給我的問題。

感謝

+0

您可以隨時獲取Gson源代碼並查看它們實際上在該方法中執行的操作。 – 2011-05-10 21:42:50

回答

5

這是遠離漂亮,但我使用對於現在的解決方案是使用

JsonObject jsonObject = gson.toJsonTree(container).getAsJsonObject(); 

建立一個JSONObject。然後我打電話給:

jsonObject.remove("animals"); 
jsonObject.add("animals",gson.toJsonTree(container.getAnimals())); 

和waa laa,對象的正確json形式。

加分點:我有嵌套的容器的列表,所以我不得不建立一個JsonArray,這樣我可以遍歷我的容器,叫我在每個自定義的toJSON()。

這個故事的寓意:使用

jsonObject.remove(); 
jsonObject.add(propertyName, property); 

招添加接口列表和遍歷(只是名單上的toJSON使用()使用JsonArray容器的列表不打電話給你的孩子上特殊的方法容器)。

肯定還是尋找一個更自然的解決方案。

快樂編碼

+0

這似乎不適用於Scala Lists tho不幸的。 – onejigtwojig 2011-11-17 06:07:42

0

如原來的問題描述GSON的最新版本仍表現。 (我認爲如果其中一個不存在,我會記錄一個增強請求。)

對於它的價值,Jackson反序列化了兩個示例數據結構。第二個例子中的ContainerAnimal實現類只需要getter。

// output: {"animals":[{"name":"betty"},{"name":"fred"}]} 

import java.io.StringWriter; 
import java.util.ArrayList; 

import org.codehaus.jackson.map.ObjectMapper; 

public class JacksonFoo 
{ 
    public static void main(String[] args) throws Exception 
    { 
    Container container = new Container(); 
    container.addAnimal(new Cat("betty")); 
    container.addAnimal(new Dog("fred")); 
    ObjectMapper mapper = new ObjectMapper(); 
    StringWriter writer = new StringWriter(); 
    mapper.writeValue(writer, container); 
    System.out.println(writer); 
    } 
} 

class Container 
{ 
    ArrayList<Animal> animals = new ArrayList<Animal>(); 

    public void addAnimal(Animal a) 
    { 
    animals.add(a); 
    } 

    public ArrayList<Animal> getAnimals() 
    { 
    return animals; 
    } 
} 

interface Animal 
{ 
    public void nothing(); 
} 

class Cat implements Animal 
{ 
    private String name; 

    public Cat(String name) 
    { 
    this.name = name; 
    } 

    public Cat() 
    { 
    } 

    @Override 
    public void nothing() 
    { 
    } 

    public String getName() 
    { 
    return name; 
    } 
} 

class Dog implements Animal 
{ 
    private String name; 

    public Dog(String name) 
    { 
    this.name = name; 
    } 

    public Dog() 
    { 
    } 

    @Override 
    public void nothing() 
    { 
    } 

    public String getName() 
    { 
    return name; 
    } 
} 
+0

謝謝指向傑克遜的指針。下次我和json一起玩時,我會看看它! – 2011-06-12 18:16:26

+1

這是Gson的一個已知問題。第170期最初記錄於2009年10月。據報道,計劃在下一個版本中包含問題231的實施,這個問題很快就會得到解決。 – 2011-06-19 05:05:20

+3

唉,它是2014年和[問題231](https://code.google.com/p/google-gson/issues/detail?id=231)仍然沒有解決:( – dimo414 2014-04-20 05:11:29

3

這是不夠的,只是使用自定義JsonSerializer明確獲取該對象的類(大概GSON的越來越聲明的類型,而不是出於某種原因)。下面是串行接口的通用解決方案:

public static class InterfaceSerializer<T> implements JsonSerializer<T> { 
    public JsonElement serialize(T link, Type type, 
           JsonSerializationContext context) { 
     // Odd Gson quirk 
     // not smart enough to use the actual type rather than the interface 
     return context.serialize(link, link.getClass()); 
    } 
} 

對於你的榜樣,我可以做到以下幾點:

GsonBuilder gbuild = new GsonBuilder(); 
Gson standard = gbuild.create(); 

ArrayList<Animal> animals = Lists.newArrayList(new Cat("Betty"),new Dog("Fred")); 
System.out.println(standard.toJson(animals)); 

Container c = new Container(); 
c.animals.addAll(animals); 
System.out.println(standard.toJson(c)); 

Gson interfaceAware = gbuild 
    .registerTypeAdapter(Animal.class, new InterfaceSerializer<>()).create(); 
System.out.println(interfaceAware.toJson(c)); 

此輸出:

[{"name":"Betty","hates":"Everything"},{"name":"Fred","loves":"Everything"}] 
{"animals":[{},{}]} 
{"animals":[{"name":"Betty","hates":"Everything"}, "name":"Fred","loves":"Everything"}]} 

的最後一項是正確序列化的字符串。

這是不夠的反序列化 JSON,不幸的是,因爲生成的JSON不包含任何類型的信息。查看How to serialize a class with an interface?的這個答案,以便跟蹤對象的類型,從而序列化/反序列化對象。