简体   繁体   中英

How do unmarshalling to different objects depending on the XML received?

I have a web service which receives XML files and does unmarshalling to convert the XML file to an object. The problem is that my web service can receive different XML structures and has to do the correct unmarshalling.

So I receibe this kind of XML files:

<root>
    <user>
        <id>1234</id>
    </user>
    <XMLelements>
        ...
    </XMLelements>
</root>

Notice that XMLelements is an example tag name, every XML file could have differents names.

So, I think, my web service workflow should be something like:

  1. It receives XML file.
  2. It checks user id.
    • If user id == 1234
      • UserA userA = unmarshalling(XMLFile)
    • Else if user id == 5678
      • UserB userB = unmarshalling(XMLFile)
    • Else
      • UserC userC = unmarshalling(XMLFile)

So I receive different XML structures and for each one I have to do a different unmarshalling to get objects of different classes.

How can I perform this approach? I'm using Spring for marshalling (Metro).

Edit: This question didn't receive any answer, maybe I'm not clear. I'll try to explain it better:

I have a web service which is listening a url where receives XML files. Actually, in the same url path my web service receives two different XML schemas. How can I know how to do unmarshalling correctly? I mean, unmarshalling should return correct object when different XML schemas are passed.

There are a couple of different ways to support this use case.


OPTION #1 - DOM Approach

You could always use a DOM parser to convert the XML to a Document perform an XPath against it to get the value of the id element and then unmarshal the document based on the result


OPTION #2 - SAX Approach

LookAheadUnmarshallerHandler

You could leverage a SAX parser and JAXB's UnmarshallerHandler mechanism and do the following:

  • Create a ContentHandler that queues up SAX events until the necessary information is discovered.
  • Create/retrieve a JAXBContext based on the value of the id element.
  • Create an UnmarshallerHandler from the JAXBContext .
  • Call the queued events on the UnmarshallerHandler .
  • Set the UnmarshallerHandler on the XMLReader as the ContentHandler .
  • Retrieve the unmarshalled object from the UnmarshallerHandler .
package forum13397834;

import java.util.*;
import javax.xml.bind.*;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;

public class LookAheadUnmarshallerHandler extends DefaultHandler {

    private XMLReader xmlReader;
    private List<Event> events = new ArrayList<Event>();
    private UnmarshallerHandler unmarshallerHandler;

    public LookAheadUnmarshallerHandler(XMLReader xmlReader) {
        this.xmlReader = xmlReader;
    }

    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException {
        events.add(new StartElement(uri, localName, qName, attributes));
    }

    @Override
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        if("id".equals(localName) || "id".equals(qName)) {
            Characters characters = (Characters) events.get(events.size() - 1);
            String value = characters.getString();
            JAXBContext jc;
            try {
                if("1234".equals(value)) {
                    jc = JAXBContext.newInstance(Root1.class);
                } else if("5678".equals(value)) {
                    jc = JAXBContext.newInstance(Root2.class);
                } else {
                    jc = JAXBContext.newInstance(Root3.class);
                }
                unmarshallerHandler = jc.createUnmarshaller().getUnmarshallerHandler();
            } catch(JAXBException e) {
                throw new RuntimeException(e);
            }
            unmarshallerHandler.startDocument();
            for(Event event : events) {
                event.event(unmarshallerHandler);
            }
            unmarshallerHandler.endElement(uri, localName, qName);
            xmlReader.setContentHandler(unmarshallerHandler);
        } else {
            events.add(new EndElement(uri, localName, qName));
        }
    }

    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        events.add(new Characters(ch, start, length));
    }

    public Object getResult() throws JAXBException {
        return unmarshallerHandler.getResult();
    }

    private static abstract class Event {

        public abstract void event(ContentHandler contentHandler) throws SAXException; 

    }

    private static class StartElement extends Event {

        private String uri;
        private String localName;
        private String qName;
        private Attributes attributes;

        public StartElement(String uri, String localName, String qName, Attributes attributes) {
            this.uri = uri;
            this.localName = localName;
            this.qName = qName;
            this.attributes = attributes;
        }

        @Override
        public void event(ContentHandler contentHandler) throws SAXException {
            contentHandler.startElement(uri, localName, qName, attributes);
        }

    }

    private static class Characters extends Event {

        private char[] ch;
        private int start;
        private int length;

        public Characters(char[] ch, int start, int length) {
            this.ch = ch;
            this.start = start;
            this.length = length;
        }

        @Override
        public void event(ContentHandler contentHandler) throws SAXException {
            contentHandler.characters(ch, start, length);
        }

        public String getString() {
            return new String(ch, start, length);
        }

    }

    private static class EndElement extends Event {

        private String uri;
        private String localName;
        private String qName;

        public EndElement(String uri, String localName, String qName) {
            this.uri = uri;
            this.localName = localName;
            this.qName = qName;
        }

        @Override
        public void event(ContentHandler contentHandler) throws SAXException {
            contentHandler.endElement(uri, localName, qName);
        }

    }

}

JAVA MODEL

The following object model is based on the classes you mention in your question.

UserA

package forum13397834;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="root")
public class UserA {

}

UserB

package forum13397834;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="root")
public class UserB {

}

UserC

package forum13397834;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="root")
public class UserC {

}

INPUT (input.xml)

We will use the XML document from your question as the input for this example.

<root>
    <user>
        <id>1234</id>
    </user>
    <XMLelements>
        ...
    </XMLelements>
</root>

DEMO CODE

package forum13397834;

import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        SAXParserFactory spf = SAXParserFactory.newInstance();
        spf.setNamespaceAware(true);
        XMLReader xmlReader = spf.newSAXParser().getXMLReader();

        LookAheadUnmarshallerHandler handler = new LookAheadUnmarshallerHandler(xmlReader);
        xmlReader.setContentHandler(handler);
        xmlReader.parse(new InputSource("src/forum13397834/input.xml"));
        Object object = handler.getResult();
        System.out.println(object.getClass());
    }

}

Output

class forum13397834.UserA

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM