2015-09-26 53 views
4

我搜索了很多,只發現關於地圖內部多態反序列化的問題。是否有可能對映射本身進行多態反序列化?是否有可能在傑克遜de /序列化映射本身多態?

例如,我有一個Book類包含一個Map作爲成員變量。

public class Book { 
    @JsonProperty 
    private Map<String, Object> reviews; 

    @JsonCreator 
    public Book(Map<String, Object> map) { 
     this.reviews = map; 
    } 
} 

另一個類有一個Book類的列表。

public class Shelf { 

    @JsonProperty 
    private List<Book> books = new LinkedList<>(); 

    public void setBooks(List<Book> books) { 
     this.books = books; 
    } 

    public List<Book> getBooks() { 
     return this.books; 
    } 
} 

和一個測試類。一本書的評論地圖是一個Hashtable,另一本書的評論地圖是一個HashMap。

public class Test { 

    private Shelf shelf; 

    @BeforeClass 
    public void init() { 
     Map<String, Object> review1 = new Hashtable<>(); // Hashtable here 
     review1.put("test1", "review1"); 
     Map<String, Object> review2 = new HashMap<>(); // HashMap here 
     review2.put("test2", "review2"); 

     List<Book> books = new LinkedList<>(); 
     books.add(new Book(review1)); 
     books.add(new Book(review2)); 
     shelf = new Shelf(); 
     shelf.setBooks(books); 
    } 

    @Test 
    public void test() throws IOException{ 
     ObjectMapper mapper = new ObjectMapper(); 
     mapper.configure(SerializationFeature.INDENT_OUTPUT, true); 
//  mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 
     String json = mapper.writeValueAsString(shelf); 
     System.out.println(json); 

     Shelf sh = mapper.readValue(json, Shelf.class); 
     for (Book b : sh.getBooks()) { 
      System.out.println(b.getReviews().getClass()); 
     } 
    } 
} 

測試輸出

{ 
    "name" : "TestShelf", 
    "books" : [ { 
    "reviews" : { 
     "test1" : "review1" 
    } 
    }, { 
    "reviews" : { 
     "test2" : "review2" 
    } 
    } ] 
} 
class java.util.LinkedHashMap 
class java.util.LinkedHashMap 

序列化工作正常。但是在反序列化之後,review1和review2都是LinkedHashMap。我希望review1和review2是它們的實際類型,它們是Hashtable來審查1和HashMap來審查2。有什麼辦法可以做到這一點?

我不想使用mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);,因爲它會爲json消息中的所有json屬性添加類型信息。如果還有更好的方法,我也不想使用定製的解串器。提前致謝。

回答

1

我張貼在傑克遜用戶論壇的問題,他們建議定製TypeResolverBuilder並將其設置在ObjectMapper實例。

ObjectMapper.setDefaultTyping(...)

我定製TypeResolverBuilder低於它解決了我的問題。

public class MapTypeIdResolverBuilder extends StdTypeResolverBuilder { 

    public MapTypeIdResolverBuilder() { 
    } 

    @Override 
    public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, 
                JavaType baseType, Collection<NamedType> subtypes) { 
     return useForType(baseType) ? super.buildTypeDeserializer(config, baseType, subtypes) : null; 
    } 

    @Override 
    public TypeSerializer buildTypeSerializer(SerializationConfig config, 
               JavaType baseType, Collection<namedtype> subtypes) { 
     return useForType(baseType) ? super.buildTypeSerializer(config, baseType, subtypes) : null; 
    } 

    /** 
    * Method called to check if the default type handler should be 
    * used for given type. 
    * Note: "natural types" (String, Boolean, Integer, Double) will never 
    * use typing; that is both due to them being concrete and final, 
    * and since actual serializers and deserializers will also ignore any 
    * attempts to enforce typing. 
    */ 
    public boolean useForType(JavaType t) { 
     return t.isMapLikeType() || t.isJavaLangObject(); 
    } 
} 

該解決方案既需要服務器端和客戶端使用定製TypeResolverBuilder。我知道這並不理想,但這是迄今爲止我發現的最佳解決方案。解決方案的詳細信息可以在我的博客上的post中找到。

0

readValue調用不知道輸入JSON來自哪裏。它不知道它是從HashtableHashMapTreeMap或任何其他類型的Map生成的。所有它必須處理的是目標類型Shelf及其嵌套的Book。傑克遜可以從Book反思的唯一事情是它有一個Map類型的字段。

Map是一個接口。既然你不能實例化一個接口,Jackson必須對它想要使用的Map的實現類型做出決定。默認情況下,它使用LinkedHashMap。您可以按照發布的解決方案here更改默認值。

另一種方法是用具體的類型來聲明領域你想

private HashMap<String, Object> reviews; 

現在傑克遜知道的JSON反序列化到一個HashMap。顯然,這隻適用於單一類型。

實際的解決方案是你不關心實際的實現類。你決定這將是一個Map。你不應該關心它在封面下使用的實現。使用多態的力量。

(請注意,使用的Hashtable早已泄氣。)

+0

非常感謝您的回覆。我實際上正在重新設計遺留代碼以將其置於RESTful API之後。代碼的一部分與我發佈的代碼具有相似的結構。它有自己的Map實現,它擴展了Hashtable。它重寫get()方法總是返回地圖中值的字符串表示。如果我將它留給Jackson的默認LinkedHashMap,則get()方法將返回Integer(如果該值爲Integer),這將打破代碼的功能。 –