简体   繁体   English

在XStream中有更好的方法来编组/解组List <Object> 在JSON和Java中

[英]In XStream is there a better way to marshall/unmarshall List<Object>'s in JSON and Java

I'm using XStream and JETTISON's Stax JSON serializer to send/receive messages to/from JSON javascripts clients and Java web applications. 我正在使用XStream和JETTISON的Stax JSON序列化程序向/从JSON javascripts客户端和Java Web应用程序发送/接收消息。

I want to be able to create a list of objects to send to the server and be properly marshalled into Java but the format that XStream and JSON expect it in is very non-intuitive and requires our javascript libraries to jump through hoops. 我希望能够创建一个发送到服务器的对象列表,并将其正确编组到Java中,但XStream和JSON期望它的格式非常不直观,并且需要我们的javascript库跳过箍。

[EDIT Update issues using GSON library] I attempted to use the GSON library but it cannot deserialize concrete objects when I only have it expect generic super classes (XStream and Jettison handles this because type information is baked into the serialization). [使用GSON库编辑更新问题]我试图使用GSON库但是当我只有它期望泛型超类时,它不能反序列化具体对象(XStream和Jettison处理这个因为类型信息被烘焙到序列化中)。

GSON FAQ states Collection Limitation : GSON FAQ声明收集限制

Collections Limitations 收藏限制

Can serialize collection of arbitrary objects but can not deserialize from it 可以序列化任意对象的集合,但不能从中反序列化

Because there is no way for the user to indicate the type of the resulting object 因为用户无法指示结果对象的类型

While deserializing, Collection must be of a specific generic type 在反序列化时,Collection必须是特定的泛型类型

Maybe I'm using bad java practices but how would I go about building a JSON to Java messaging framework that sent/received various concrete Message objects in JSON format? 也许我正在使用糟糕的java实践,但我如何构建一个JSON到Java消息传递框架,以JSON格式发送/接收各种具体的Message对象?

For example this fails: 例如,这失败了:

public static void main(String[] args) {
    Gson gson = new Gson();

    MockMessage mock1 = new MockMessage();
    MockMessage mock2 = new MockMessage();
    MockMessageOther mock3 = new MockMessageOther();

    List<MockMessage> messages = new ArrayList<MockMessage>();
    messages.add(mock1);
    messages.add(mock2);
    messages.add(mock3);

    String jsonString = gson.toJson(messages);

    //JSON list format is non-intuitive single element array with class name fields
    System.out.println(jsonString);
    List gsonJSONUnmarshalledMessages = (List)gson.fromJson(jsonString, List.class);
    //This will print 3 messages unmarshalled
    System.out.println("XStream format JSON Number of messages unmarshalled: " + gsonJSONUnmarshalledMessages.size());
}

[{"val":1},{"val":1},{"otherVal":1,"val":1}]
Exception in thread "main" com.google.gson.JsonParseException: The JsonDeserializer com.google.gson.DefaultTypeAdapters$CollectionTypeAdapter@638bd7f1 failed to deserialized json object [{"val":1},{"val":1},{"otherVal":1,"val":1}] given the type interface java.util.List

Here's an example, I want to send a list of 3 Message objects, 2 are of the same type and the 3rd is a different type. 这是一个例子,我想发送一个包含3个Message对象的列表,2个是相同类型的,3是不同类型。

import java.util.ArrayList;
import java.util.List;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver;

class MockMessage {
    int val = 1;
}
class MockMessageOther {
    int otherVal = 1;
}

public class TestJSONXStream {


    public static void main(String[] args) {
        JettisonMappedXmlDriver xmlDriver = new JettisonMappedXmlDriver();        
        XStream xstream = new XStream(xmlDriver);

        MockMessage mock1 = new MockMessage();
        MockMessage mock2 = new MockMessage();
        MockMessageOther mock3 = new MockMessageOther();

        List messages = new ArrayList();
        messages.add(mock1);
        messages.add(mock2);
        messages.add(mock3);

        String jsonString = xstream.toXML(messages);

        //JSON list format is non-intuitive single element array with class name fields
        System.out.println(jsonString);
        List xstreamJSONUnmarshalledMessages = (List)xstream.fromXML(jsonString);
        //This will print 3 messages unmarshalled
        System.out.println("XStream format JSON Number of messages unmarshalled: " + xstreamJSONUnmarshalledMessages.size());

        //Attempt to deserialize a reasonable looking JSON string
        String jsonTest = 
              "{"+
                "\"list\" : ["+ 
                          "{"+
                          "\"MockMessage\" : {"+
                              "\"val\" : 1"+
                          "}"+
                      "}, {"+
                          "\"MockMessage\" : {"+
                              "\"val\" : 1"+
                          "}"+
                      "}, {"+
                          "\"MockMessageOther\" : {"+
                              "\"otherVal\" : 1"+
                          "}"+
                      "} ]"+
                  "};";

        List unmarshalledMessages = (List)xstream.fromXML(jsonTest);

        //We expect 3 messages but XStream only deserializes one
        System.out.println("Normal format JSON Number of messages unmarshalled: " + unmarshalledMessages.size());
    }

}

Intuitively I expect the XStream JSON to be serialized (and able to deserialize correctly) from the following format: 直观地说,我希望从以下格式序列化XStream JSON(并能够正确地反序列化):

{
    "list" : [ 
        {
        "MockMessage" : {
            "val" : 1
        }
    }, {
        "MockMessage" : {
            "val" : 1
        }
    }, {
        "MockMessageOther" : {
            "otherVal" : 1
        }
    } ]
}

Instead XStream creates a single element list with fields that are named the classnames and nested arrays of Objects of the same type. 相反,XStream会创建一个单独的元素列表,其中包含名为类名的字段和相同类型的对象的嵌套数组。

{
    "list" : [ {
        "MockMessage" : [ {
            "val" : 1
        }, {
            "val" : 1
        } ],
        "MockMessageOther" : {
            "otherVal" : 1
        }
    } ]
}

The trouble may be caused by it using the XStream XML CollectionConverter ? 使用XStream XML CollectionConverter可能导致问题?

Does anyone have a suggestion for a good JSON Java object serialization that allows you to read/write arbitrary Java objects. 有没有人建议一个好的JSON Java对象序列化,允许你读/写任意Java对象。 I looked at the Jackson Java JSON Processor but when you were reading in objects from a stream you had to specify what type of object it was unlike XStream where it will read in any object (because the serialized XStream JSON contains class name information). 我查看了Jackson Java JSON处理器,但是当您从流中读取对象时,您必须指定它与XStream不同的对象类型,它将在任何对象中读取(因为序列化的XStream JSON包含类名信息)。

I agree with other poster in that XStream is not a good fit -- it's an OXM (Object/Xml Mapper), and JSON is handled as a secondary output format using XML processing path. 我同意其他海报,因为XStream不太合适 - 它是一个OXM(对象/ Xml映射器),JSON使用XML处理路径作为辅助输出格式处理。 This is why a "convention" (of how to convert hierarchich xml model into object-graph model of json and vice versa) is needed; 这就是为什么需要一个“约定”(如何将层次结构xml模型转换为json的对象图模型,反之亦然); and your choice boils down to using whatever is least intrusive of sub-optimal choices. 而你的选择归结为使用任何次要的次优选择。 That works ok if XML is your primary data format, and you just need some rudimentary JSON(-like) support. 如果XML是您的主要数据格式,那么这是可行的,您只需要一些基本的JSON(类似)支持。

To get good JSON-support, I would consider using a JSON processing library that does real OJM mapping (I assume Svenson does too, but additionally), such as: 为了获得良好的JSON支持,我会考虑使用一个真正的OJM映射的JSON处理库(我假设Svenson也这样做,但另外),例如:

Also: even if you do need to support both XML and JSON, you are IMO better off using separate libraries for these tasks -- objects (beans) to use on server-side need not be different, just serialization libs that convert to/from xml and json. 另外:即使你确实需要同时支持XML和JSON,你最好还是为这些任务使用单独的库 - 在服务器端使用的对象(bean)不需要不同,只是转换为/从的序列化库xml和json。

I realize this is off-topic, but I'd like to present a solution in svenson JSON . 我意识到这是偏离主题的,但我想在svenson JSON中提出一个解决方案。

Do you really need public fields in your domain classes? 您真的需要域类中的公共字段吗? Apart from having to use properties, svenson can handle cases like this with a more simple JSON output with a discriminator property 除了必须使用属性之外,svenson还可以使用带有discriminator属性的更简单的JSON输出来处理这样的情况

class Message
{
   // .. your properties with getters and setters .. 
   // special property "type" acts a signal for conversion
}

class MessageOther
{
  ...
}

List list = new ArrayList();
list.add(new Message());
list.add(new MessageOther());
list.add(new Message());

String jsonDataSet = JSON.defaultJSON().forValue(list);

would output JSON like 会输出JSON之类的

[
    {"type":"message", ... }, 
    {"type":"message_other", ... }, 
    {"type":"message", ... }
]

which could be parsed again with code like this 可以使用这样的代码再次解析

    // configure reusable parse instance
    JSONParser parser = new JSONParser();

    // type mapper to map to your types
    PropertyValueBasedTypeMapper mapper = new PropertyValueBasedTypeMapper();
    mapper.setParsePathInfo("[]");
    mapper.addFieldValueMapping("message", Message.class);
    mapper.addFieldValueMapping("message_other", MessageOther.class);
    parser.setTypeMapper(mapper);

    List list = parser.parse(List.class, jsonDataset);

A svenson type mapper based on the full class name would look something like this 基于完整类名的svenson类型映射器看起来像这样

public class ClassNameBasedTypeMapper extends PropertyValueBasedTypeMapper
{
    protected Class getTypeHintFromTypeProperty(String value) throws IllegalStateException
    {
        try
        {
            return Class.forName(value);
        }
        catch (ClassNotFoundException e)
        {
            throw new IllegalStateException(value + " is no valid class", e);
        }
    }
}

which is not an ideal implementation as it inherits the configuration of PropertyValueBasedTypeMapper without really needing. 这不是一个理想的实现,因为它在没有真正需要的情况下继承了PropertyValueBasedTypeMapper的配置。 (should include a cleaner version in svenson) (应该在svenson中包含一个更清洁的版本)

The setup is very much like above 设置非常类似于上面

JSONParser parser = new JSONParser();
ClassNameBasedTypeMapper mapper = new ClassNameBasedTypeMapper();
mapper.setParsePathInfo("[]");
parser.setTypeMapper(mapper);

List foos = parser
    .parse( List.class, "[{\"type\":\"package.Foo\"},{\"type\":\"package.Bar\"}]");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM