javaandroidxmlsimple-framework

Can I use SimpleXML to parse XML whose structure is unknown?


I am using SimpleXML to parse small XML files used in a communication protocol. This all works fine, but now I am implementing a part of the protocol which includes a kind of free-form XML.

For example, an XML like this:

<telegram>
  <config>
    <foo>yes</foo>
    <bar>no</bar>
  </config>
</telegram>

Where foo and bar might change in the future, or an element baz might be added, without the need to touch the parsing code. I would like to access these elements in Java using a construct like

tree.getConfig().get("bar");   // returns "no"

Can I use SimpleXML to parse that? I looked into the documentation, but couldn't find what I need.


Solution

  • Can I use SimpleXML to parse that?

    Not out of the box - but writing a Converter will do it.


    @Root(name = "telegram")
    @Convert(Telegram.TelegramConverter.class) // Requires AnnotationStrategy
    public class Telegram
    {
        private Map<String, String> config;
    
    
        public String get(String name)
        {
            return config.get(name);
        }
    
        public Map<String, String> getConfig()
        {
            return config;
        }
    
        // ...
    
        @Override
        public String toString()
        {
            return "Telegram{" + "config=" + config + '}';
        }
    
    
    
    
        static class TelegramConverter implements Converter<Telegram>
        {
            @Override
            public Telegram read(InputNode node) throws Exception
            {
                Telegram t = new Telegram();
    
                final InputNode config = node.getNext("config");
                t.config = new HashMap<>();
    
                // Iterate over config's child nodes and put them into the map
                InputNode cfg = config.getNext();
    
                while( cfg != null )
                {
                    t.config.put(cfg.getName(), cfg.getValue());
                    cfg = config.getNext();
                }
    
                return t;
            }
    
            @Override
            public void write(OutputNode node, Telegram value) throws Exception
            {
                // Implement if you need serialization too
                throw new UnsupportedOperationException("Not supported yet.");
            }
    
        }
    }
    

    Usage:

    final String xml = "<telegram>\n"
            + "  <config>\n"
            + "    <foo>yes</foo>\n"
            + "    <bar>no</bar>\n"
            + "    <baz>maybe</baz>\n" // Some "future element"
            + "  </config>\n"
            + "</telegram>";
    /*
     * The AnnotationStrategy is set here since it's
     * necessary for the @Convert annotation
     */
    Serializer ser = new Persister(new AnnotationStrategy());
    Telegram t = ser.read(Telegram.class, xml);
    
    System.out.println(t);
    

    Result:

    Telegram{config={bar=no, foo=yes, baz=maybe}}