2017-06-01 72 views
1

我在當前項目中遇到了GSON多態對象的奇怪情況。情況是這樣的:我有兩個不同的使用情況下,兩個不同的抽象基類:GSON不包含區分字段基本列表對象列表

  • 第一類包含在其本身列表中,並
  • 第二類也包含列表中的,但該列表是一個更大的父對象的一部分

做作類的簡化版本(構造並且爲了簡潔省略了存取器;鑑別字段中定義但註釋了用於說明):

public abstract class ClassNumeric { 
    //private String numericType; 
} 

public class ClassOne extends ClassNumeric { 
    private String hex; 
} 

public class ClassTwo extends ClassNumeric { 
    private Integer whole; 
    private Integer fraction; 
} 

public abstract class ClassAlphabetic { 
    //private String alphabeticType; 
} 

public class ClassAlpha extends ClassAlphabetic { 
    private String name; 
} 

public class ClassBravo extends ClassAlphabetic { 
    private Integer age; 
    private Integer numberOfMarbles; 
} 

public class Container { 
    private String group; 
    private List<ClassAlphabetic> alphabetics; 
} 

適配器工廠和他們的登記與GSON:

public RuntimeTypeAdapterFactory<ClassNumeric> numericTypeFactory = RuntimeTypeAdapterFactory 
    .of(ClassNumeric.class, "numericType") 
    .registerSubtype(ClassOne.class) 
    .registerSubtype(ClassTwo.class); 

public RuntimeTypeAdapterFactory<ClassAlphabetic> alphabeticTypeFactory = RuntimeTypeAdapterFactory 
    .of(ClassAlphabetic.class, "alphabeticType") 
    .registerSubtype(ClassAlpha.class) 
    .registerSubtype(ClassBravo.class); 

public final Gson gson = new GsonBuilder() 
    .setPrettyPrinting() 
    .disableHtmlEscaping() 
    .registerTypeAdapterFactory(numericTypeFactory) 
    .registerTypeAdapterFactory(alphabeticTypeFactory) 
    .create(); 

基於我到目前爲止閱讀,我沒有(實際上不應該)在基地申報鑑別場因爲GSON在內部處理這些內容,因爲JSON是序列化和反序列化的。

下面是如何這些可以使用的一個示例:

ClassOne c1 = ClassOne.builder().hex("EF8A").build(); 
ClassTwo c2 = ClassTwo.builder().whole(1).fraction(3).build(); 
List<ClassNumeric> numerics = Arrays.asList(c1, c2); // List of child objects 
log.debug("Numerics: " + gson.toJson(numerics)); 

ClassAlpha ca = ClassAlpha.builder().name("Fred").build(); 
ClassBravo cb = ClassBravo.builder().age(5).numberOfMarbles(42).build(); 
List<ClassAlphabetic> alphas = Arrays.asList(ca, cb); 
Container container = Container.builder().group("Test Group 1").alphabetics(alpha).build(); // List of objects field on larger object 
log.debug("Alphas (container): " + gson.toJson(container)); 

時遇到的問題是,ClassAlphabetic對象正常工作(鑑別字段存在於JSON),而ClassNumeric對象不要(歧視字段缺失)。示例輸出:

09:12:17.910 [main] DEBUG c.s.s.s.s.GSONPolymorphismTest - Numerics: [ 
    { 
     "hex": "EF8A" 
    }, 
    { 
     "whole": 1, 
     "fraction": 3 
    } 
] 
09:12:17.926 [main] DEBUG c.s.s.s.s.GSONPolymorphismTest - Alphas (container): { 
    "group": "Test Group 1", 
    "alphabetics": [ 
     { 
      "alphabeticType": "ClassAlpha", 
      "name": "Fred" 
     }, 
     { 
      "alphabeticType": "ClassBravo", 
      "age": 5, 
      "numberOfMarbles": 42 
     } 
    ] 
} 

我在這裏錯過了什麼?這些基本上是用GSON進行定義和設置的,但其中一個用例在另一個用例中不起作用。

回答

1

這是因爲泛型在Java中的工作原理。簡而言之,特定的泛型類實例沒有任何類型參數化信息作爲實例的一部分。但是,類型參數可以存儲在字段類型,方法返回類型,方法參數,繼承的超類(例如,extends ArrayList<Integer>),自定義參數化類型信息實例等中。局部變量(如numerics)在編譯時保留類型參數並存在於你和編譯器的頭腦中 - 由於type erasure。所以,它就像運行時的​​原始List。與numerics類似,alphas沒有任何運行時參數化,但與本地變量不同,Container.alphabetics字段具有在運行時保留的類型信息。 - 字段可以提供完整的類型信息。這由Gson用來確定應用哪個(de)序列化策略。同樣,當沒有提供附加類型參數信息時(如局部變量),Gson使用默認策略。正如我上面提到的,您還可以創建自定義參數化類型ParameterizedType以提供原始類型及其類型參數信息。它如何幫助?如果仔細看看Gson toJson過載,可以看到它的一個重載accepts有一個額外的參數(我選擇了最簡單的一個參數)。這可以認爲是Gson告訴它傳入實例的確切類型(不一定匹配,但在大多數情況下應該匹配)的一種提示。所以,這只是爲了工作,告訴你GSON numericsList類型參數:

// "Canonical" way: TypeToken analyzes its superclass parameterization and returns its via the getType method 
private static final Type classNumericListType = new TypeToken<List<ClassNumeric>>() { 
}.getType())); 

System.out.println("Numerics: " + gson.toJson(numerics, classNumericListType); 

或者,如果你可以使用GSON 2.8.0+,

private static final Type classNumericListType = TypeToken.getParameterized(List.class, ClassNumeric.class); 

System.out.println("Numerics: " + gson.toJson(numerics, classNumericListType); 

或者乾脆創建你自己的ParameterizedType實現。如果類型在編譯時已知,則會使用類型標記;如果只在運行時才知道類型,則使用TypeToken.getParameterized。有了這樣的,傳遞類型實例觸發RuntimeTypeAdapterFactory機制(現在是ClassNumeric,而不是原始Object),所以輸出如下:

Numerics: [ 
    { 
    "numericType": "N1", 
    "hex": "EF8A" 
    }, 
    { 
    "numericType": "N2", 
    "whole": 1, 
    "fraction": 3 
    } 
]