2011-04-16 72 views
4

我正在創建一個以JSON消息的形式接收請求的服務。我需要解析消息並根據請求類型採取適當的操作。例如(在僞代碼):基於對象類型的反序列化JSON

switch(request.type) { 
    case "NewOrder": 
     createNewOrder(order); 
     break; 
    case "CancelOrder" 
     cancelOrder(orderId); 
     break; 
} 

似乎大多數JSON的API(至少是那些爲你做的對象映射)需要根對象類型進行反序列化。有沒有優雅的方法呢?

舉例來說,傑克遜的API(使用全對象映射),我需要調用映射如下:

NewOrder newOrder = mapper.readValue(src, NewOrder.class); 
CancelOrder cancelOrder = mapper.readValue(src. CancelOrder.class); 

這意味着我需要知道對象的類我已經解析甚至在它。我真正需要的是一些方法來不期而遇的JSON字符串,確定該請求類型,然後調用相應的readValue()方法 - 是這樣的:

String requestType = getRequestType(src); 
switch(request.type) { 
    case "NewOrder": 
     NewOrder newOrder = mapper.readValue(src, NewOrder.class); 
     createNewOrder(newOrder.order); 
     break; 
    case "CancelOrder" 
     CancelOrder cancelOrder = mapper.readValue(src. CancelOrder.class); 
     cancelOrder(cancelOrder.orderId); 
     break; 
} 

是否有可能做到這一點使用傑克遜或其他任何Java JSON解析器?我確信我可以進入較低級別並使用流式API或基於節點的API,但如果可以的話,儘量避免這種複雜性。

回答

4

如果使用Jackson的JSON輸入解析成地圖,您可以快速訪問類型信息。然後,您可以創建該對象作爲所需的類,並使用ObjectMapper.convert來配置您從Jackson獲得的Map中的對象。

下面是一個例子:

public class Test1 { 
private String f; 
private String b; 

public void setFoo(String v) { f = v; } 

public void setBim(String v) { b = v; } 

public String toString() { return "f=" + f + ", b=" + b; } 


public static void main(String[] args) throws Exception { 
    String test = "{ \"foo\":\"bar\", \"bim\":\"baz\" }"; 
    ObjectMapper mapper = new ObjectMapper(); 
    HashMap map = mapper.readValue(new StringReader(test), HashMap.class); 
    System.out.println(map); 
    Test1 test1 = mapper.convertValue(map, Test1.class); 
    System.out.println(test1); 
} 
} 
+0

剛剛開始使用Jackson,所以後續問題是Jackson API用於創建MAP的原因 - 就是他們的流API或Tree Model API。另外,如果傑克遜經歷了創建地圖的麻煩,我可以窺探請求類型並將剩下的地圖交給傑克遜來構造「包裝」對象 - 而不是使用BeanUtils? – Naresh 2011-04-16 18:24:16

+0

是的,你可以!我編輯了我的答案來展示一個例子。 – 2011-04-16 21:29:40

+0

太棒了 - 這應該適合我!與此同時,我制定了類似的解決方案。很快就會發布。唯一的區別是我讀了JsonNode的樹而不是HashMap。 – Naresh 2011-04-16 21:54:53

3

你可以使用一個包裝您的訂單各地:

{ 
"NewOrder": {...} 
} 

{ 
"CancelOrder": {...} 
} 

UPDATE:

class Wrapper { 
    newOrder: NewOrder; 
    cancelOrderId: Integer; 
} 

Wrapper wrapper = mapper.readValue(src, Wrapper.class); 

if (wrapper.newOrder != null) { 
    createNewOrder(wrapper.newOrder); 
} 
if (wrapper.cancelOrderId != null) { 
    cancelOrder(wrapper.cancelOrderId); 
} 
+0

包裝怎麼幫我解決這個問題?請注意,NewOrder請求的內容是完整訂單,而CancelOrder請求的內容僅僅是一個訂單ID。一般來說,這些請求可能會有非常不同的內容。例如,另一個請求可能是SubmitUserProfile,其中包含用戶配置文件。 – Naresh 2011-04-16 18:21:00

+0

我已經完成我的回覆 – 2011-04-17 07:29:24

+0

啊我明白了!所以包裝包裝所有可能的請求(有點像聯合),並且(通常)只有其中一個將不爲空。好的模式 - 我必須記住這一點。謝謝Maurice。 – Naresh 2011-04-17 14:45:13

0

假設訂單僅僅是數據,責任下放給DoSomethingService和生產通過工廠的服務可以幫助:

Service service = takeActionFactory 
    .buildTheRightServiceForTheValue(src); 
service.takeAction(); 

工廠將會解析JSON對象:

Service buildTheRightServiceForTheValue(src) { 
    switch(request.type) { 
    case "NewOrder": 
     return new NewOrderService(mapper.readValue(src, NewOrder.class)); 
     break; 
    case "CancelOrder" 
     return new CancelOrderService(mapper.readValue(src. CancelOrder.class)); 
     break; 
    } 
    case "SomeOtherObject" 
     return new SomeOtherService(mapper.readValue(src, SomeOtherService.class)); 
    } 

而具體的服務是服務子分類:

NewOrderService implements Service { 
    private final NewOrder newOrder; 
    /**constructor*/ 
    ... 

    void takeAction() { 
     createNewOrder(newOrder.order); 
    } 
} 
+0

我肯定會得到你的服務理念(這是Command模式),但問題在於「switch(request.type)」。由於請求類型嵌入在請求本身(即src)中,所以在src解析(至少部分)之前它是不可用的。 – Naresh 2011-04-16 19:10:24

+0

好的。在那種情況下,我對Jackson的幫助不夠了解。我想知道如果問題出現了一些,因此服務承擔了太多的責任。如果您可以有不同的URL或請求參數來區分可能有幫助的不同事件。否則,Maurice Perry的回答也可以使用:JSON消息中的一些附加類型信息可以被傳遞(類型名稱或其他)以幫助決定要採取的行動。 – nzroller 2011-04-16 19:30:30

+0

閱讀完所有回覆後,我有幾點想法(你的,西蒙和佩裏的)。致力於解決方案。當我有合理的東西時我會發布。 – Naresh 2011-04-16 20:54:19

0

感謝Maurice,Simon和nzroller。結合你所有回答的想法,這就是我提出的解決方案。反饋歡迎。

public enum MessageType { 
    NewOrder, 
    CancelOrder 
} 

public class JsonMessage { 
    private MessageType messageType; 
    private Object payload; 
    ... 
} 

public class Order { 
    private String orderId; 
    private String itemId; 
    private int quantity; 
    ... 
} 

public class NewOrder { 
    private Order order; 
    ... 
} 

public class CancelOrder { 
    private String orderId; 
    ... 
} 

下面是如何序列化NewOrder:

JsonMessage jsonMessage = 
    new JsonMessage(MessageType.NewOrder, newOrder); 
ObjectMapper mapper = new ObjectMapper(); 
String jsonMessageString = mapper.writeValueAsString(jsonMessage); 

反序列化,我第一次看到了JSON字符串JsonNode的樹。然後我讀取messageType。最後,基於消息類型,我直接讀取有效負載作爲Java對象。

JsonNode rootNode = mapper.readValue(jsonMessageString, JsonNode.class); 

MessageType messageType = 
    MessageType.valueOf(rootNode.path("messageType").getTextValue()); 

switch(messageType) { 
    case NewOrder: 
     NewOrder newOrder = mapper.readValue(
       rootNode.path("payload"), NewOrder.class); 
     myOrderService.placeOrder(newOrder.getOrder()); 
     break; 

    case CancelOrder: 
     CancelOrder cancelOrder = mapper.readValue(
       rootNode.path("payload"), CancelOrder.class); 
     myOrderService.cancelOrder(cancelOrder.getOrderId()); 
     break; 
}