xStream JSON - how to handle null values

旧街凉风 提交于 2019-12-13 04:41:46

问题


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:

  1. Extend the com.thoughtworks.xstream.converters.reflection.ReflectionConverter class.
  2. 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).
  3. 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.
  4. Extend the com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriter and com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper classes to provide a custom writer. A single method is needed to write down a null value node.
  5. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!