2012-08-16 43 views
1

我正在使用Java和Jersey編寫一個RESTful Web服務,其中服務將接受XML或JSON輸入。 Jackson被用作JSON解串器,並被集成到Jersey配置中。Jackson相當於@XmlSeeAlso

其中一個端點是對URL的POST請求,其中內容可以是幾個不同Java類之一,並且有一個公共基類。這些類 - 與XML註釋 - 是:

@XmlRootElement(name = "action") 
@XmlAccessorType(XmlAccessType.NONE) 
@XmlSeeAlso({ FirstAction.class, SecondAction.class, ThirdAction.class }) 
public abstract class BaseAction { 
} 

@XmlRootElement(name = "first-action") 
@XmlAccessorType(XmlAccessType.NONE) 
public class FirstAction extends BaseAction implements Serializable { 
} 

// Likewise for SecondAction, ThirdAction 

在我的資源,我可以聲明等的方法:

@POST 
@Path("/{id}/action") 
public Response invokeAction(@PathParam("id") String id, BaseAction action) {...} 

然後我可以發佈,看起來像<firstAction/>一個XML片段,我的方法將被調用與FirstAction實例。到現在爲止還挺好。

我在掙扎的地方是讓JSON反序列化與XML反序列化一樣無縫地工作。如果@XmlSeeAlso註釋對於使XML反序列化正常工作至關重要,那麼JSON的等效似乎是@JsonSubTypes。所以我註解類是這樣的:

// XML annotations removed for brevity, but they are present as in the previous code snippet 
@JsonSubTypes({ @JsonSubTypes.Type(name = "first-action", value = FirstAction.class), 
    @JsonSubTypes.Type(name = "second-action", value = SecondAction.class), 
    @JsonSubTypes.Type(name = "third-action", value = ThirdAction.class) }) 
public abstract class BaseAction { 
} 

@JsonRootName("first-action") 
public class FirstAction extends BaseAction implements Serializable { 
} 

// Likewise for SecondAction, ThirdAction 

然後我給它我的測試輸入:{ "first-action": null }但所有我能得到的是:

「org.codehaus.jackson.map.JsonMappingException:根目錄名稱「第一-action'與類型[簡單類型,類com.alu.openstack.domain.compute.server.actions.BaseAction]不符合預期('action')「

不幸的是,因爲我試圖與別人的API我無法更改我的示例輸入 - { "first-action": null }必須工作,並將類FirstAction的對象傳遞給我的方法。 (該動作沒有任何字段,這就是爲什麼null不應該成爲問題 - 這是重要的類的類型)。

以與XML反序列化相同的方式使JSON反序列化工作的正確方式是什麼?

回答

0

我調查了使用@JsonTypeInfo,但遇到了問題,因爲我無法改變輸入格式。解析器必須能夠處理輸入{ "first-action":null }。這排除了添加@type@class屬性的可能性。使用包裝器對象可能已經工作,但它在有效載荷上窒息。

關鍵的一點是我使用的是UNWRAP_ROOT_PROPERTY配置選項。傑克遜絕對堅持要找到action財產,我無法考慮其他任何事情。所以,我必須有選擇地禁用某些域對象的UNWRAP_ROOT_PROPERTY,這樣Jackson纔會開放解析備選方案。我修改了項目的ContextResolver.getContext(...)實現來檢查@JsonRootName註釋 - 因爲這隻有在啓用了包裝時纔有意義,因此我使用此註釋的存在來確定是否返回配置有根屬性包裝的對象映射器開或關。

在這個階段,我可能已經能夠使用@JsonTypeInfo(include=JsonTypeInfo.As.WRAPPER_OBJECT, ...),除了上面提到的null有效載荷的問題(這是用來指示子對象沒有屬性 - 如果我從工作規範曾給一個空的對象{},而不會有問題)。所以繼續我需要一個自定義類型的解析器。

我創建了一個擴展org.codehaus.jackson.map.TypeDeserializer的新類,其目的是每當調用Jackson來反序列化BaseAction實例時,它都會調用此自定義解串器。反序列化器將被賦予一個子類型數組,其中BaseAction將first-action,second-action等映射到FirstAction.class等。解串器爲字段名稱讀取輸入流,然後將該名稱與類匹配。如果下一個標記是一個對象,那麼它會查找並委託給該類的適當的反序列化器,或者如果它爲空,它會找到無參數構造函數並調用它來獲取對象。

實現org.codehaus.jackson.map.jsontype.TypeResolverBuilder的類需要能夠建立這個以前類的一個實例,然後將TypeResolverBuilder被給定爲一個@JsonTypeResolver註釋上BaseAction類。

2

如果您使用的是傑克遜,您正在尋找@JsonTypeInfo@Type。請參閱here以獲取更多信息

+0

@ JsonTypeInfo似乎是用增加字段來增加內容以包含類型信息。正如問題中所述,我正在編寫其他人的API的兼容實現,所以我無法更改模式,並且不幸的是添加字段不是一個選項。除非我錯過@ JsonTypeInfo可以做的事 - 在這種情況下,你可以舉個例子嗎? – 2012-08-16 12:08:47

+0

請看'@ JsonTypeInfo'。這是你需要的。它不需要在POJO中添加屬性;它只會將它包含在JSON中,這是你可以控制的東西。另外,不要試圖保持XML和JSON看起來儘可能相同:JSON和XML數據模型是不同的(阻抗),因此相同邏輯內容的自然結構自然不同。 – StaxMan 2012-08-16 17:13:21

1

JSON不支持XML的工作方式,因此解決方案不相同。

你需要使用的是(像其他答案說的),@JsonTypeInfo。這隻會觸發包含和使用類型標識符。如果是這樣,那麼'@ JsonSubTypes'將用於反序列化。

該指標必須使用的原因很簡單:如果您有多個可供反序列化的替代類型,則必須有區別。

請注意,這不一定是屬性 - 而大多數用戶選擇「As.PROPERTY」包含,它不是(IMO)最好的方式。 「WRAPPER_OBJECT」可能是你正在尋找的東西,因爲它增加了一個額外的中間JSON屬性,這與XML有些類似。