我正在使用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或o @Path
注释成员添加到类'A')都失败了.我查看了SimpleXML文档,显然使用时存在以下限制@Text
:
管理Text注释使用的规则是每个模式类只能有一个.此外,此注释不能与Element注释一起使用.只有属性注释可以与它一起使用,因为此注释不会在拥有元素中添加任何内容.
@ElementList(entry = "a", inline = true, required = false) ListaList;
再一次,"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("").append(root.getName()).append(">"); } }
注意:在此解决方案中,'a'标记也将被解析为最终字符串.为避免这样做,可以为根节点发出特殊情况concatNodesTree()方法.