Deserializing an XML tag with text AND subtags using Retrofit

我只是一个虾纸丫 提交于 2019-12-03 08:46:58

Though this question doesn't seem to have attract much attention, I'm sharing a solution that I've found for this problem anyways - perhaps others could benefit.

Apparently the makers of the Simple XML framework were aware of the fact that some XML's don't fit into their predefined, standard cases (much like in my case). Therefore, they've added support in serialization/deserialization overriding. One can create a custom converter class and use the @Convert annotation to apply it over specific XML constructs. Within the custom converter, the XML deserialization is 'reduced' to an API very similar to the standard Java org.w3c.dom framework.

In order to solve the problem introduced in my question, the following code can be used:

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

The CustomConverter in essence concatenates the content of the text directly under the 'a' and the text under 'b' onto A's content data member.

Taking another step forward

In the interest of full disclosure, I'd also like to share the real solution I ended up going for, which was for a generalization of the problem I asked about in this post.

The content under the anonymous 'a' tag that I had to deserialize was in fact HTML-tagged text. For example:

<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 tags are irrelevant for the parsing of the XML as a whole: All I really needed was for the content under 'a' to be deserialized as plain-text under a class named 'A'. So here's my (recursive) converter:

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

Note: In this solution, the 'a' tag would also be parsed into the final string. To avoid doing that, one can issue a special case concatNodesTree() method for the root node.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!