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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/836805/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 13:58:13  来源:igfitidea点击:

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

javaajaxjsonserializationxstream

提问by Dougnukem

I'm using XStreamand 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 GSONlibrary] I attempted to use the GSON librarybut 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 常见问题说明收集限制

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

集合限制

可以序列化任意对象的集合但不能反序列化

因为用户没有办法指明结果对象的类型

反序列化时,集合必须是特定的泛型类型

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. I looked at the Hymanson Java JSON Processorbut 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).

有没有人有关于一个好的 JSON Java 对象序列化的建议,它允许您读/写任意 Java 对象。我查看了Hymanson Java JSON 处理器,但是当您从流中读取对象时,您必须指定它不同于 XStream 的对象类型,它可以读取任何对象(因为序列化的 XStream JSON 包含类名信息)。

采纳答案by StaxMan

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. This is why a "convention" (of how to convert hierarchich xml model into object-graph model of json and vice versa) is needed; 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.

我同意其他海报,因为 XStream 不太合适——它是一个 OXM(对象/Xml 映射器),并且使用 XML 处理路径将 JSON 作为辅助输出格式处理。这就是为什么需要“约定”(关于如何将层次结构 xml 模型转换为 json 的对象图模型,反之亦然)的原因;并且您的选择归结为使用次优选择中干扰最少的方法。如果 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 支持,我会考虑使用一个 JSON 处理库来进行真正的 OJM 映射(我假设 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,对于这些任务,您最好在 IMO 中使用单独的库——在服务器端使用的对象(bean)不必不同,只需转换为/从的序列化库xml 和 json。

回答by fforw

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 还可以使用带有鉴别器属性的更简单的 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);

回答by fforw

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. (should include a cleaner version in svenson)

这不是一个理想的实现,因为它在没有真正需要的情况下继承了 PropertyValueBasedTypeMapper 的配置。(应该在 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\"}]");