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

时间:2009-05-07 20:14:28

标签: java ajax json serialization xstream

我正在使用XStream和JETTISON的Stax JSON序列化程序向/从JSON javascripts客户端和Java Web应用程序发送/接收消息。

我希望能够创建一个要发送到服务器的对象列表,并将其正确编组到Java中,但XStream和JSON期望它的格式非常不直观,并且需要我们的javascript库来跳过箍。

[使用GSON库编辑更新问题] 我试图使用GSON library但它不能反序列化具体对象,因为我只期望泛型超类(XStream和Jettison处理这个因为类型信息被烘焙到序列化中)。

GSON FAQ states Collection Limitation

  

收藏限制

     

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

     

因为用户无法指明结果对象的类型

     

在反序列化时,Collection必须是特定的泛型类型

也许我正在使用糟糕的java实践,但是我如何构建一个JSON到Java消息传递框架,以JSON格式发送/接收各种具体的Message对象?

例如,这失败了:

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

这是一个例子,我想发送一个包含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());
    }

}

直观地说,我期望从以下格式序列化(并能够正确反序列化)XStream JSON:

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

而是XStream创建一个单独的元素列表,其中的字段名为类名和相同类型的对象的嵌套数组。

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

使用XStream XML CollectionConverter

可能会导致问题

是否有人建议使用允许您读/写任意Java对象的良好JSON Java对象序列化。我查看了Jackson Java JSON Processor但是当您从流中读取对象时,您必须指定它与XStream不同的对象类型,它将在任何对象中读取(因为序列化的XStream JSON包含类名信息)。

3 个答案:

答案 0 :(得分:6)

我同意其他海报,因为XStream不太合适 - 它是一个OXM(Object / Xml Mapper),JSON使用XML处理路径作为辅助输出格式处理。这就是为什么需要一个“约定”(如何将层次结构xml模型转换为json的对象图模型,反之亦然);而你的选择归结为使用任何次要的次优选择。 如果XML是您的主要数据格式,那么这是正常的,您只需要一些基本的JSON(类似)支持。

为了获得良好的JSON支持,我会考虑使用一个真正的OJM映射的JSON处理库(我假设Svenson也这样做,但另外),例如:

另外:即使你确实需要同时支持XML和JSON,你最好还是为这些任务使用单独的库 - 在服务器端使用的对象(bean)不需要不同,只是转换的序列化库来自xml和json。

答案 1 :(得分:0)

我意识到这是偏离主题的,但我想在svenson JSON中提出一个解决方案。

您真的需要域类中的公共字段吗?除了必须使用属性之外,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);

会输出JSON,如

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

可以使用像这样的代码再次解析

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

答案 2 :(得分:0)

基于完整类名的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);
        }
    }
}

这不是一个理想的实现,因为它在没有真正需要的情况下继承了PropertyValueBasedTypeMapper的配置。 (应该在svenson中包含更清洁的版本)

设置与上面的内容非常相似

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