xStream JSON - 如何处理空值

发布于 2024-12-11 10:48:07 字数 792 浏览 2 评论 0原文

我有这个 json 示例:

{
    "rows": [
        {
            "anaid": "1",
            "anaint": "123",
            "anastring": "-"
        },
        {
            "anaid": "2",
            "anaint": "-",
            "anastring": "Hello World"
        },
        {
            "anaid": "3",
            "anaint": "-",
            "anastring": "-"
        }
    ]
}

在我的类 Test 中,其中有:

  • int anaid
  • int anaint
  • String anastring

我的 java 代码:

XStream xstream = new XStream(new JettisonMappedXmlDriver());
xstream.alias("rows", Test.class);
ArrayList<Test> product = (ArrayList<Test>) xstream.fromXML(json);

所以字符“-”是我的 json 的空值。我无法更改代码来创建 json,但我会处理“-”空值以正确使用我的 xStream 解析器。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

风渺 2024-12-18 10:48:07

根据 XStream 的文档和邮件列表,空值不包含在编组输出中——这可能是一种约定。

讨论帮助我弄清楚了如何显示它们。简而言之,窍门就是 Varun 所说的:自定义转换器,用于扩展默认的 ReflectionConverter。这个想法是停止忽略空值事件并将它们写入输出。

这是关于它如何对我起作用的详细解释。不幸的是我无法分发最终的解决方案;我希望以下内容能够解锁许多内容,甚至导致在主树中的集成。

食谱

这是我的解决方案的要点,基于[1] 和 XStream 1.4.2:

  1. 扩展 com.thoughtworks.xstream.converters.reflection.ReflectionConverter 类。
  2. 重写doMarshal/3方法。原始方法忽略空事件,因此不写入任何内容。新方法应该只获取这些事件并写入所需的内容(空节点、自关闭节点)。
  3. 重写 writeField/5 方法来写入空值节点。写什么取决于想要什么,并且应该委托给 writer 对象。
  4. 扩展 com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriter 和 com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper 类以提供自定义编写器。需要一个方法来记下空值节点。
  5. 以非常低的优先级注册转换器(因为反射是基础并且会接管其他转换器)。

有关该方法的更多详细信息

例如,可以扩展 XStream 提供的编写器并实现一个额外的接口:

// 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
    }
}

该接口位于 NullExtendedHierarchicalStreamWriter 中,它添加了一个 emptyNode/2 方法到 ExtendedHierarchicalStreamWriter。此方法将空值写入自关闭节点(例如,对于 XML ,意味着 num 是空整数)。上面的代码片段使用帮助器 NullExtendedHierarchicalStreamWriterHelper 获取此编写器的实例,它本身是 ExtendedHierarchicalStreamWriterHelper 的简单扩展。请注意,传递给帮助器的编写器是 writer.underlyingWriter(),因为编写器被包装并且包装器没有实现接口的 null 扩展——这意味着我的解决方案仍然是一个 hack,直到进行了更深入的整合。

转换器的注册通常是这样完成的:

serializer = new XStream(new MyStreamDriver());

MyReflectionConverter reflectionConverter = new
        MyReflectionConverter(serializer.getMapper(),
                              serializer.getReflectionProvider());
serializer.registerConverter(reflectionConverter, XStream.PRIORITY_VERY_LOW);

这里是上述自定义反射转换器与原始代码的差异。请考虑,这是一个扩展,不属于主树的一部分,并且它比原始代码经过的测试要少得多。迄今为止,它已经通过了测试套件的案例。

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.

丿*梦醉红颜 2024-12-18 10:48:07

对我来说,简单的方法是获取源代码,复制 ReflexionConverted 类并在方法中 public void marshal(Object original, Final HierarchicalStreamWriter writer, Final MarshallingContext context) ...

你有:

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

        }
    }
}

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

        }
    }
}
我的奇迹 2024-12-18 10:48:07

使用自定义转换器。教程此处

Use a custom converter. Tutorial here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文