根据对象类型反序列化 JSON

发布于 2024-11-02 03:20:13 字数 1144 浏览 4 评论 0原文

我正在创建一个以 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;
}

Is it possible to do this using Jackson or any other Java JSON parser?我确信我可以进入较低的级别并使用流 API 或基于节点的 API,但如果可以的话,尽量避免这种复杂性。

I am creating a service that receives requests in the form of JSON messages. I need to parse the message and take the appropriate action based on the request type. For example (in pseudo code):

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

It seems that most JSON APIs (at least those that do the object mapping for you) need the root object type to deserialize. Is there any elegant way around this?

As an example, in the Jackson API (using full object mapping), I need to call the mapper as follows:

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

Which means that I need to know the object's class even before I have parsed it. What I really need is some way to peek into the JSON string, determine the request type and then call the appropriate readValue() method - something like this:

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

Is it possible to do this using Jackson or any other Java JSON parser? I am sure I can go to a lower level and use a streaming API or a node based API, but trying to avoid that complexity if I can.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

浅沫记忆 2024-11-09 03:20:13

如果您使用 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);
}
}

If you use Jackson to parse the JSON input into a Map, you can quickly access the type information. You can then create the object as the required class and use ObjectMapper.convert to configure the object from the Map you got from Jackson.

Here is an example:

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);
}
}
爱殇璃 2024-11-09 03:20:13

您可以在订单周围使用包装器:

{
"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);
}

You could use a wrapper around your order:

{
"NewOrder": {...}
}

or

{
"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);
}
尬尬 2024-11-09 03:20:13

假设 Order 只是数据,将责任委托给 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));
    }

具体服务是 Service 的子类:

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

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

Assuming the Order is just data, delegating responsibility to a DoSomethingService and producing the service via a factory might help:

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

The factory would parse the JSON object:

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));
    }

And the concrete services are sub-classes of Service:

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

    void takeAction() {
        createNewOrder(newOrder.order);
    }
}
笑咖 2024-11-09 03:20:13

谢谢莫里斯、西蒙和 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;
}

Thanks Maurice, Simon and nzroller. Combining ideas from all your responses here's the solution I came up with. Feedback welcome.

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;
    ...
}

Here's how to serialize a NewOrder:

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

To deserialize, I first read the JSON string into a tree of JsonNode's. I then read the messageType. Finally, based on the message type, I read the payload directly as a Java object.

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文