2015-02-10 88 views
2

我想要做的是以下內容:給定一個JSON文檔,將其映射到使用Jackson的POJO,但基於JSON文檔中的字段定義通用類成員的類型。傑克遜反序列化映射到Java泛型

我的JSON如下所示

{ 
    "name": "Name", 
    "parameters": [ 
        {"name": "paramName","value": "Value1", "@type": "string"}, 
        {"name": "size","value": 5,"@type": "double"} 

       ] 
} 

映射到此JSON文檔類是

public class Strategy { 
    public String name; 
    public List<Parameter<?>> parameters; 
} 

然後,我有一個通用類此如下

public class Parameter<T> { 
    public String name; 
    public T value; 

    @Override 
    public String toString() { 
     return this.getClass().getName(); 
    } 
} 

所以這個想法是在您將JSON文檔反序列化到Strategy類並獲取參數實際時告訴Jackson d,使用以下類作爲值成員的通用數據類型,即我想將它選爲String或Double或Integer,但我希望這是我的決定,以便它是通用的,並且可以擴展爲任何數據類型I想。

我知道我可以使用註釋JsonTypeInfo我加入,以及這樣

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="@type") 

但使用這些類的其實是工作,但傑克遜決定自己是什麼類型的,應根據其價值和我的尺寸參數設置爲整​​數。如果我將它的值設置爲5.0,那麼它的設置爲Double,但是如果我想讓其中一個參數成爲自定義對象,該怎麼辦?

我可以得到這個工作的唯一方法(並且對解決方案並非100%滿意)是使Parameter類抽象,然後爲每個我想要的類型創建具體的類,即ParameterString,ParameterDouble,ParameterCustomClass和然後使用@JsonSubTypes註釋根據JSON文檔中的類型字段設置要使用的正確類。

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="@type") 
@JsonSubTypes({ 
    @JsonSubTypes.Type(value=ParameterString.class, name="string"), 
    @JsonSubTypes.Type(value=ParameterDouble.class, name="double"), 
    @JsonSubTypes.Type(value=ParameterInstrument.class, name="instrument") 
}) 

具有以下類爲例

public class StrategyParameterString extends StrategyParameter<String> { 

} 

這是不是很可擴展的,我想這將只是需要一個新的亞型的註釋,並增加了每一個類型的,我需要具體的類,但只是不覺得它可能是優雅的。

有沒有人知道更好的處理方法?

感謝 安德魯

+0

我只是好奇你爲什麼要這樣做。 Java通過擦除來實現泛型。這意味着泛型類型參數僅在編譯時存在並且具有含義。它們在運行時不存在。 'List >'的運行時''''對象是'List.class'。如果您需要將類型信息與不可修飾類型相關聯,則建議您創建一個類型安全的異構容器並將其存儲在您的列表中,例如'列表'。 – scottb 2015-02-10 16:04:25

+0

感謝您的意見@scottb。基本上,我通過最初爲每個需要的類型創建一個類,讓它們實現一個接口並使用List 來解決這個問題,那裏有兩個問題,首先當我遍歷列表時,對象不知道關於類成員,除非我施放它,其次每個類都有重複的代碼,所以我將它重構爲一個基本的抽象類,以減少代碼重複。我想具體的類實現在編譯時設置類型,這可能是由於Java擦除而更好地處理它的方法。 – 2015-02-10 16:45:58

回答

0

據我瞭解,你想在你Parameter列表代表類型reifiable,例如。字符串,雙,儀器。您可以利用可重用類型具有類文字形式的運行時類型標記這一事實。這可以被利用來形成異類型安全收集的基礎。

而不是定義您的參數類這樣的:

public class Parameter<T> { 
    public String name; 
    public T value; 
    : 
    : 
    } 
} 

您可以將它定義對象的價值與它的運行時類型的令牌相關聯的具體類。

public class Parameter() { 
    private final Object m_value; 
    private final Class<?> m_cls; 

    private Parameter(Class<?> token, Object val) { 
     m_value = val; 
     m_cls = token; 
    } 

    public static <T> Parameter newInstance(Class<T> token, T value) { 
     return new Parameter(token, value); 
    } 
    : 
    : 

    public <T> T getValue(Class<T> token) { 
     if (token != m_cls) throw new ClassCastException("Type error"); 
     return token.cast(m_value); 
    } 
} 

在此設置中,類型標記和通用的方法(而不是一個通用型)用於設置和重新建立爲所期望的值的類型鍵。您設置的值可以是任何類型,並且只要類型標記保持一致,就可以保證以與您存儲的類型相同的方式返回。

請注意,構造函數不能是通用的。爲了解決這個問題,Parameter的構造函數已經變爲private,並且通過調用newInstance()靜態工廠方法(可以是通用的)來形成Parameter實例。

+0

感謝@scottb的詳細解釋,非常感謝。只是所以我理解你在上面寫的這個Parameter類,它所具有的2個字段是值(m_value)和值表示的類(m_cls),這就是爲什麼getValue方法將值轉換爲正確的類類型的原因?我想知道這是否會與傑克遜反序列化一起工作? – 2015-02-11 11:21:48

+0

沒有...沒有工作也沒有解決您的問題 – eugen 2015-02-12 05:31:55

+0

'Class'是可序列化的(並且類是可驗證的)。如果你的項目中的其他引用類型也是可序列化的,我不明白爲什麼這不能起作用。 'getValue()'方法中的比較可能需要省略。問題在於主機平臺上的檢查聲明對象值的類型與正在請求的類型相同。可能無法在客戶端上運行。如果該檢查被忽略,那麼演員陣容將被取消選中;不能保證您會請求與存儲的類型相同的類型。 – scottb 2015-02-12 15:20:01