Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple XML - 2 elements, same name different namespace

I need to parse a XML string into objects. I would use SimpleXML for it but i got an error, Duplicate annotation of name 'link' on field 'url' private java.lang.String com.example.rogedormans.xmlreader.XML.Alert.Channel.url.

An sample XML with the same problem:

<rss........>
     <channel>
         <title>The Title</title>
         <link>http://www.someurl.com</link>
         <description>Some description</description>
         <atom:link href="http://dunno.com/rss.xml" rel="self" type="application/rss+xml"/>
        ....
        ....
    </channel>
 </rss>

I googled a lot and found This, this and this stackoverflow article but none worked for me...I also read the Simple XML documentation but i can't get it working.

How can i get both of the "link" items into my object? (i think it is something with the namespace, but what?)

An code example would be nice!!!

like image 690
CodeNinja Avatar asked Oct 30 '22 10:10

CodeNinja


1 Answers

You can workaround this by implementing a Converter for the Channel class.


Here's an example for you. It lacks any kind of error checking etc. and is reduced to Channel class with a single Atom only. But you'll see how it works.

Class Channel with Converter

@Root()
@Convert(Channel.ChannelConverter.class) // Specify the Converter
public class Channel
{
    @Element
    private String title;
    @Element
    private String link;
    @Element
    private String description;
    @Namespace(reference = "http://www.w3.org/2005/Atom", prefix = "atom")
    @Element()
    private AtomLink atomLink;

    // Ctor's / getter / setter ...


    static class ChannelConverter implements Converter<Channel>
    {
        @Override
        public Channel read(InputNode node) throws Exception
        {
            Channel channel = new Channel();
            InputNode child;

            // Iterate over all childs an get their values
            while( ( child = node.getNext() ) != null )
            {
                switch(child.getName())
                {
                    case "title":
                        channel.setTitle(child.getValue());
                        break;
                    case "description":
                        channel.setDescription(child.getValue());
                        break;
                    case "link":
                        /*
                         * "link" can be either a <link>...</link> or
                         * a <atom:link>...</atom:link>
                         */
                        if( child.getPrefix().equals("atom") == true )
                        {
                            AtomLink atom = new AtomLink();
                            atom.setHref(child.getAttribute("href").getValue());
                            atom.setRel(child.getAttribute("rel").getValue());
                            atom.setType(child.getAttribute("type").getValue());
                            channel.setAtomLink(atom);
                        }
                        else
                        {
                            channel.setLink(child.getValue());
                        }
                        break;
                    default:
                        throw new RuntimeException("Unknown Element found: " + child);
                }
            }

            return channel;
        }


        @Override
        public void write(OutputNode node, Channel value) throws Exception
        {
            /*
             * TODO: Implement if necessary
             */
            throw new UnsupportedOperationException("Not supported yet.");
        }

    }


    @Root
    public static class AtomLink
    {
        @Attribute
        private String href;
        @Attribute
        private String rel;
        @Attribute
        private String type;

        // Ctor's / getter / setter ...
    }
}

All inner classes can be implemented as usual classes too. If things are complex to (de-)serialize you can use a Serializer within the Converter too.

Usage

And finally a demo:

final String xml = "     <channel>\n"
        + "         <title>The Title</title>\n"
        + "         <link>http://www.someurl.com</link>\n"
        + "         <description>Some description</description>\n"
        + "         <atom:link href=\"http://dunno.com/rss.xml\" rel=\"self\" type=\"application/rss+xml\" xmlns:atom=\"http://www.w3.org/2005/Atom\" />\n"
        + "        ....\n"
        + "        ....\n"
        + "    </channel>\n";

Serializer ser = new Persister(new AnnotationStrategy());
//                             ^----- Important! -----^
Channel c = ser.read(Channel.class, xml);
System.out.println(c);

Please note that the Converter requires a Strategy (see link above for more details); I have used the AnnotationStrategy since you can simply use @Convert() then. The xmlns has to be defined somewhere in your XML, else you'll catch an Exception; I put it into the <atom:link … /> here.

Output

Channel{title=The Title, link=http://www.someurl.com, description=Some description, atomLink=AtomLink{href=http://dunno.com/rss.xml, rel=self, type=application/rss+xml}}
like image 58
ollo Avatar answered Nov 08 '22 14:11

ollo