我使用Retrofit和SimpleXML来解析某些公共API的XML响应。我对所有内容做得很好,直到我偶然发现包含自由文本和子标记的XML标记 - 如下例所示:
<a>
Some free-style text
<b>Even more text!</b>
</a>
为了尝试使用Simple-XML注释进行反序列化,我已经采取了两种方式。请注意,基本上&#39; a 列表的条目标记:
@ElementList(entry = "a", inline = true, required = false) List<A> aList;
拥有&#39; A&#39;定义如下:
public static class A {
@Text(required = false) protected String a;
}
这很好地读取了自由文本部分,但任何尝试反序列化&#39;&#39;标记(例如,通过将@Element
w或w / o @Path
注释成员添加到类&#39; A&#39;)已失败。我查看了SimpleXML文档,显然使用@Text
存在限制:
管理Text注释使用的规则是每个模式类只能有一个。此外,此注释不能与Element注释一起使用。只有属性注释可以与它一起使用,因为此注释不会在拥有元素中添加任何内容。
@ElementList(entry = "a", inline = true, required = false) List<String> aList;
然而,&#39; a&#39;标签得到了正确的反序列化,但是没有办法找到&#39; b&#39;的内容。子标签。
&#39; a&#39;的内容如何?标签与其关联的&#39;&#39;反序列化。使用纯JAVA对象的简单XML注释的子标签?
答案 0 :(得分:5)
虽然这个问题似乎没有引起太多关注,但我仍然在分享我为这个问题找到的解决方案 - 也许其他人可能会受益。
显然,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()方法。