基于对象类型反序列化JSON
我正在创建一个以JSON消息的forms接收请求的服务。 我需要解析消息并根据请求类型采取适当的操作。 例如(伪代码):
switch(request.type) { case "NewOrder": createNewOrder(order); break; case "CancelOrder" cancelOrder(orderId); break; }
似乎大多数JSON API(至少那些为您执行对象映射的API)需要根对象类型来反序列化。 这周围有什么优雅的方式吗?
例如,在Jackson API(使用完整对象映射)中,我需要调用mapper,如下所示:
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; }
是否可以使用Jackson或任何其他Java JSON解析器执行此操作? 我确信我可以使用流API或基于节点的API进入较低级别,但如果可以的话,尽量避免这种复杂性。
如果您使用Jackson将JSON输入解析为Map,则可以快速访问类型信息。 然后,您可以将对象创建为必需的类,并使用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); } }
您可以在订单周围使用包装:
{ "NewOrder": {...} }
要么
{ "CancelOrder": {...} }
更新:
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); }
假设订单只是数据,将责任委托给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); } }
谢谢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; }