问题
I've this json example:
{
"rows": [
{
"anaid": "1",
"anaint": "123",
"anastring": "-"
},
{
"anaid": "2",
"anaint": "-",
"anastring": "Hello World"
},
{
"anaid": "3",
"anaint": "-",
"anastring": "-"
}
]
}
where in my class Test are:
- int anaid
- int anaint
- String anastring
My java code:
XStream xstream = new XStream(new JettisonMappedXmlDriver());
xstream.alias("rows", Test.class);
ArrayList<Test> product = (ArrayList<Test>) xstream.fromXML(json);
So the character "-" is the null value for my json. I can't change the code to create json, but I would handling the "-" null value to use correctly my xStream parser.
回答1:
According to XStream's documentation and mailing list, null values are not included in the marshaling output---perhaps as a convention.
A discussion helped me figure out how to display them. In short, the trick is what Varun said: A custom converter, to extend the default ReflectionConverter. The idea is to stop ignoring null value events and write them to the output.
Here is a detailed explanation on how that worked for me. Unfortunately I cannot distribute the final solution; I hope the following unlocks many, or even leads to integration in the main tree.
Recipe
Here is the gist of my solution, based on [1] and XStream 1.4.2:
- Extend the
com.thoughtworks.xstream.converters.reflection.ReflectionConverter
class. - Override the
doMarshal
/3 method. The original method ignores null events and so writes nothing. The new method should just take those events and write what is wanted (an empty node, a self-closing node). - Override the
writeField
/5 method to write the null value nodes. What to write depends of what is wanted, and should be delegated to a writer object. - Extend the
com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriter
andcom.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper
classes to provide a custom writer. A single method is needed to write down a null value node. - Register the converter, with very low priority (as reflection is fundamental and would take over other converters).
More detail on the approach
For example, one can extend a writer provided with XStream and implement an extra interface:
// Context of "MyReflectionConverter"'s doMarshal method.
void writeField(...)
if (newObj == null) {
NullExtendedHierarchicalStreamWriterHelper.emptyNode(
writer.underlyingWriter(),
aliasName != null ? aliasName:
mapper.serializedMember(source.getClass(), fieldName),
fieldType);
}
else { // Rest of the original writeField method
}
}
The interface is here NullExtendedHierarchicalStreamWriter
which adds an emptyNode
/2 method to ExtendedHierarchicalStreamWriter
. This method writes a null value as a self-closing node (e.g. for XML <int value="num"/>
, meaning num
is a null integer). The above snippet gets an instance of this writer using a helper NullExtendedHierarchicalStreamWriterHelper
, itself a simple extension of ExtendedHierarchicalStreamWriterHelper
. Note that the writer passed to the helper is writer.underlyingWriter()
, as writers are wrapped and the wrapper does not implement the null extension of the interface---this means my solution remains a hack until a deeper integration is done.
Registration of the converter is typically done this way:
serializer = new XStream(new MyStreamDriver());
MyReflectionConverter reflectionConverter = new
MyReflectionConverter(serializer.getMapper(),
serializer.getReflectionProvider());
serializer.registerConverter(reflectionConverter, XStream.PRIORITY_VERY_LOW);
Here is a diff of the above custom reflection converter, against the original code. Please consider that this is an extension that is not part of the main tree, and it is much less tested than the original code. To date it passes the cases of a test suite.
回答2:
For me the easy way is to get the source code, copy the ReflexionConverted class and in the method public void marshal(Object original, final HierarchicalStreamWriter writer, final MarshallingContext context)...
you have:
reflectionProvider.visitSerializableFields(source, new ReflectionProvider.Visitor() {
public void visit(String fieldName, Class fieldType, Class definedIn, Object newObj) {
if (newObj != null) {
...
complete the else method with your code...for example:
} else {
Object obj = new String("");
writeField(fieldName, fieldType, definedIn, obj);
seenFields.add(fieldName);
}
}
}
回答3:
Use a custom converter. Tutorial here
来源:https://stackoverflow.com/questions/7874377/xstream-json-how-to-handle-null-values