2013-01-08 81 views
3

我想使用Jakson反序列化嵌套多態類型。這意味着我的最高級別類型反映了另一個多態類型,最終由不是抽象類的類擴展。這不起作用,它會引發異常。傑克遜反序列化嵌套多態類型

這是我試圖做的一個簡化例子。

package com.adfin; 

import junit.framework.TestCase; 
import org.codehaus.jackson.annotate.JsonSubTypes; 
import org.codehaus.jackson.annotate.JsonTypeInfo; 
import org.codehaus.jackson.map.ObjectMapper; 

import java.io.IOException; 

public class JaksonDouble extends TestCase { 

    @JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME, 
    include = JsonTypeInfo.As.PROPERTY, 
    property = "name" 
) 
    @JsonSubTypes({ 
    @JsonSubTypes.Type(value = SecondLevel.class, name = "SECOND") 
    }) 
    public static abstract class FirstLevel { 
    public abstract String getTestValue(); 
    } 

    @JsonTypeInfo(
    use = JsonTypeInfo.Id.CLASS, 
    include = JsonTypeInfo.As.PROPERTY, 
    property = "@class" 
) 
    public static abstract class SecondLevel extends FirstLevel { 

    } 

    public static class FinalLevel extends SecondLevel { 
    String test; 
    @Override public String getTestValue() { return test; } 
    } 

    public void testDoubleAbstract() throws IOException { 
    String testStr = "{ \"name\": \"SECOND\", \"@class\": \"com.adfin.JasksonDouble.FinalLevel\", \"test\": \"foo\"}"; 

    ObjectMapper mapper = new ObjectMapper(); 
    FirstLevel result = mapper.readValue(testStr, FirstLevel.class); 
    } 
} 

我得到關於抽象類型的標準異常。

org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.adfin.JaksonDouble$SecondLevel, problem: abstract types can only be instantiated with additional type information at [Source: [email protected]; line: 1, column: 19] 

讓我來解釋一下我的用例。我有一個描述數據工作流的Json文檔。我在「第一級」有一個抽象類型,描述了對單個值的操作。我派生了一堆非抽象的類來實現常用操作(我使用@JsonSubTypes對它們進行了註釋)。

我有一個被稱爲「定製」特殊@JsonSubTypes。這是另一個表示別人編寫的自定義操作的抽象類(在普通jar外),它們可以使用「@class」屬性指定完全限定的類名。它看起來像Jakson解析器從不讀取第二個lavel類中的@JsonTypeInfo註釋。

我該如何做這項工作。或者至少我怎樣才能使這個用例起作用。

回答

2

你的定義搞砸了 - 你試圖使用兩個類型標識符,類型名稱和類。這沒有任何意義。你應該選擇一種方法或其他方法,而不是兩種方法。

如果選擇Java類的名字作爲類型信息,只留下了名字。此外,您只需要包含@JsonTypeInfoFirstLevel;子類繼承這個定義。

如果你喜歡使用邏輯類型的名稱,刪除類屬性。您還需要指定子類型列表,可以使用註釋或通過ObjectMapper註冊。

0

首先在你的JSON中的所有類名是錯誤的,應該是com.adfin.JasksonDouble $ FinalLevel,美元的替代點。

這裏是工作的代碼,但我不知道這是否正確答案的所有亞型的東西。

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class") 
public static abstract class FirstLevel { 
    public abstract String getTestValue(); 
} 

刪除其他類的註釋,它應該工作(只是測試它)。

然而所有這些東西看起來很複雜,如果你是自由地嘗試,你可能想看看Genson另一個庫。 要啓用多態類型支持,您必須配置您的Genson實例。如果你也是生產json的人,那麼你不需要其他東西(因爲你可以使用Genson根據需要生成json流來處理多態類型)。

下面是一個例子:

// enable polymorphic types support 
Genson genson = new Genson.Builder().setWithClassMetadata(true).create(); 
// the @class must be first property in the json object 
String testStr = "{ \"@class\": \"com.adfin.JasksonDouble$FinalLevel\", \"test\": \"foo\"}"; 
FirstLevel result = genson.deserialize(testStr, FirstLevel.class); 
System.out.println(result.getTestValue()); 

與Genson的另一個好處是,它可以讓你註冊的別名爲您的類的能力,所以你不必讓你流中的所有可用包信息。另一個好處是,如果您將json流存儲在數據庫中並將您的類移動到另一個包中,則只需更改應用中的別名,而不是從數據庫遷移所有json流。

Genson genson = new Genson.Builder().setWithClassMetadata(true) 
            .addAlias("FinalLevel", FinalLevel.class) 
            .create(); 
// the @class must be first property in the json object 
String testStr = "{ \"@class\": \"FinalLevel\", \"test\": \"foo\"}"; 
FirstLevel result = genson.deserialize(testStr, FirstLevel.class); 
System.out.println(result.getTestValue());