2011-04-16 32 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; 
}