java - 使用 Retrofit 反序列化带有文本和子标签的 XML 标签

标签 java android xml retrofit simple-framework

我正在使用 Retrofit 和 SimpleXML 来解析来自某些公共(public) API 的 XML 响应。我对所有内容都做得很好,直到我偶然发现包含自由文本和子标签的 XML 标签 - 如以下示例所示:

<a>
   Some free-style text
   <b>Even more text!</b>
</a>

为了尝试使用 Simple-XML 注释进行反序列化,我采用了两种方法。请记住,'a' 基本上是 一个列表 条目标签:

第一个:

@ElementList(entry = "a", inline = true, required = false) List<A> aList;

像这样定义“A”:

public static class A {
    @Text(required = false) protected String a;
}

这可以很好地读取自由文本部分,但是任何反序列化“b”标签内容的尝试(例如通过添加 @Element w or w/o @Path 类 'A' 的注释成员)失败。我查看了 SimpleXML 文档,显然存在使用 @Text 的限制:

The rules that govern the use of the Text annotation are that there can only be one per schema class. Also, this annotation cannot be used with the Element annotation. Only the Attribute annotation can be used with it as this annotation does not add any content within the owning element.

第二种方法,比较简单:

@ElementList(entry = "a", inline = true, required = false) List<String> aList;

再一次,“a”标签的内容得到了正确的反序列化,但是没有办法访问“b”子标签的内容。

如何使用基于 JAVA 对象的纯简单 XML 注释,将“a”标签的内容与其关联的“b”子标签反序列化?

最佳答案

虽然这个问题似乎没有引起太多关注,但我正在分享我为这个问题找到的解决方案 - 也许其他人可以从中受益。

显然,Simple XML 框架的创建者意识到一些 XML 不适合他们预定义的标准情况(很像我的情况)。因此,他们在 serialization/deserialization overriding 中添加了支持.可以创建自定义转换器类并使用 @Convert 批注将其应用于特定的 XML 构造。在自定义转换器中,XML 反序列化被“简化”为与标准 Java org.w3c.dom 框架非常相似的 API。

为了解决我问题中引入的问题,可以使用如下代码:

// First, declare the 'a' tags in the root class hosting them (this is pretty standard):
@ElementList(entry = "a", inline = true) List<A> aList;

// Second, create and apply a custom converter as described:
@Root
@Convert(CustomConverter.class)
public class A {
    String content = "";

    public String getContent() {
        return content;
    }
}

public class CustomConverter implements Converter<A> {

    @Override
    public A read(InputNode node) throws Exception {
        A a = new A();

        String value = node.getValue();
        if (value != null) {
            a.content = value;
        }

        InputNode nodeB = node.getNext();
        if (nodeB != null) {
            value = nodeB.getValue();
            if (value != null) {
                a.content += value;
            }
        }

        return a;
    }

    @Override
    public void write(OutputNode node, A value) throws Exception {
        // N/A
    }
}

CustomConverter 本质上是将 'a' 正下方的文本内容和 'b' 下方的文本连接到 A 的 content 数据成员上。

又向前迈进了一步

为了全面披露,我还想分享我最终寻求的真正解决方案,这是对我在这篇文章中提出的问题的概括。

我必须反序列化的匿名“a”标签下的内容实际上是 HTML 标签文本。例如:

<a>
If you can't explain it 
<i>simply</i>
, you don't 
<i>
   understand it 
   <b>well enough.</b>
</i>
 -- Albert Einstein
</a>

HTML 标记与整个 XML 的解析无关:我真正需要的是将“a”下的内容反序列化为名为“A”的类下的纯文本。所以这是我的(递归)转换器:

@Override
public A read(InputNode node) throws Exception {
    final StringBuilder sb = new StringBuilder(1024);
    concatNodesTree(sb, node);
    
    A a = new A();
    a.content = sb.toString();
    return a;
}

private void concatNodesTree(StringBuilder sb, InputNode root) throws Exception {

    if (root.isElement()) {
        sb.append("<").append(root.getName()).append(">");
    }

    String value = root.getValue();
    if (value != null) {
        sb.append(value);
    }

    InputNode node = root.getNext();
    while (node != null) {
        concatNodesTree(sb, node);

        // Each time a sub-tree is 'over', getValue() will deserialize the potentially upcoming free-text
        value = root.getValue();
        if (value != null) {
            sb.append(value);
        }
        node = root.getNext();
    }

    if (root.isElement()) {
        sb.append("</").append(root.getName()).append(">");
    }
}

注意:在此解决方案中,“a”标签也将被解析为最终字符串。为避免这样做,可以为根节点发出一种特殊情况的 concatNodesTree() 方法。

关于java - 使用 Retrofit 反序列化带有文本和子标签的 XML 标签,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34256195/

相关文章:

java - 我可以在 Java 中根据 XSD 片段验证 XML 文件吗

java - 为 vaadin 基本条形图中的单个条形着色

java - Java中按日期(月)排序的SQL语句

java - 如何读取 JAX-RS( Jersey )中的 cookie

java - 如何直接读取Oracle数据泵二进制转储文件?

android - 在 Android ConstraintLayout 中创建具有可变数量 n 的网格 (n × n)

xml - WIX 安装程序 : Cannot set more than one appSettings node

android - onTouchstart 事件不会在 Listview(Alloy) 中触发,只有 Android

android - Android mobile (not wear) 是否有默认的 DotsPageIndicator?

html - 为什么 XSLT 文件中没有填充背景图像