我正在创建一个以JSON消息的形式接收请求的服务。我需要解析消息并根据请求类型采取适当的操作。例如(伪代码):
switch(request.type) {
case "NewOrder":
createNewOrder(order);
break;
case "CancelOrder"
cancelOrder(orderId);
break;
}
似乎大多数JSON API(至少那些为您执行对象映射的API)需要根对象类型来反序列化。这有什么优雅的方式吗?
例如,在Jackson 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;
}
是否可以使用Jackson或任何其他Java JSON解析器执行此操作?我确信我可以使用流API或基于节点的API,但是如果可以的话,尽量避免这种复杂性。
答案 0 :(得分:4)
如果使用Jackson将JSON输入解析为Map,则可以快速访问类型信息。然后,您可以将对象创建为必需的类,并使用ObjectMapper.convert
从您从Jackson获得的地图配置对象。
以下是一个例子:
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);
}
}
答案 1 :(得分:3)
您可以在订单周围使用包装:
{
"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);
}
答案 2 :(得分: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);
}
}
答案 3 :(得分: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;
}