我正在使用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包含类名信息)。
答案 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\"}]");