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

我正在使用Retrofit和SimpleXML来解析来自某些公共API的XML响应。 我一直在很好地处理所有内容,直到我偶然发现包含自由文本和子标记的XML标记 – 如以下示例所示:

 Some free-style text Even more text!  

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

首先:

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

‘A’定义如下:

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

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

管理Text注释使用的规则是每个模式类只能有一个。 此外,此注释不能与Element注释一起使用。 只有属性注释可以与它一起使用,因为此注释不会在拥有元素中添加任何内容。

第二种方法,更简单:

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

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

如何使用与JAVA对象相关的纯Simple-XML注释,使用关联的’b’子标签对’a’标签的内容进行反序列化?

虽然这个问题似乎没有引起太多关注,但我总是分享一个我已经找到的解决方案 – 也许其他人可能会受益。

显然,Simple XML框架的制造者意识到某些XML不适合他们预定义的标准情况(就像我的情况一样)。 因此,他们在序列化/反序列化覆盖中增加了支持。 可以创建自定义转换器类并使用@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 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 { @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标记的文本。 例如:

  If you can't explain it simply , you don't  understand it well enough.  -- Albert Einstein  

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(""); } } 

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