基于对象类型反序列化JSON

时间:2011-04-16 17:23:28

标签: java json jackson

我正在创建一个以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,但是如果可以的话,尽量避免这种复杂性。

4 个答案:

答案 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;
}