Search code examples
javaxmlxsdjaxbjaxb2

XSD for having 2 root elements (1 at a time)


So here is a complex/retarded situation. I am writing an XSD and there happens to be a requirement where i need 2 root elements (1 at any given time)

<xs:element name="booksList">
    <xs:complexType>
         <xs:sequence>
             <xs:element name="book" type="bookType" minOccurs="0" maxOccurs="unbounded"/>
         </xs:sequence> 
    </xs:complexType>   
</xs:element>

and then

<xs:element name="book" type="bookType"></xs:element>

at any given time, either of these element will be used as the root element, so an XML would look like

<bookList>
<book>
<author>XYZ</author>
</book>
</bookList>

or

<book>
<author>XYZ</author>
</book>

Both of these XML will be sent back to the user from 2 different URL's i.e. the list will be sent from localhost/books.xml?author=XYZ and single book will be sent from localhost/book_name.xml

How can i achieve this with one xml ? I tried putting the book definition in the XSD but JAXB2.1 didn't generate any Book class. Is there something which i am missing ?


EDIT1: BookType has been generated but BookType doesn't have any root element.


Solution

  • XML SCHEMA

    I am writing an XSD and there happens to be a requirement where i need 2 root elements (1 at any given time)

    The XML schema below supports having the two root elements booksList and book that you are looking for.

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    
        <xs:element name="booksList">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="book" type="bookType" minOccurs="0"
                        maxOccurs="unbounded" />
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    
        <xs:element name="book" type="bookType"></xs:element>
    
        <xs:complexType name="bookType">
            <xs:sequence>
                <xs:element name="author" type="xs:string" />
            </xs:sequence>
        </xs:complexType>
    
    </xs:schema>
    

    GENERATED MODEL

    I tried putting the book definition in the XSD but JAXB2.1 didn't generate any Book class.

    Your JAXB (JSR-222) implementation will generate a class for the named complex type bookType, then for the bookElement it will create an @XmlElementDecl annotation on the ObjectFactory class.

    BooksList

    A class with an @XmlRootElement was generated on this class because it corresponds to a global element with an anonymous complex type.

    package forum11620825;
    
    import java.util.*;
    import javax.xml.bind.annotation.*;
    
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {"book"})
    @XmlRootElement(name = "booksList")
    public class BooksList {
    
        protected List<BookType> book;
    
        public List<BookType> getBook() {
            if (book == null) {
                book = new ArrayList<BookType>();
            }
            return this.book;
        }
    
    }
    

    BookType

    This class was generated to correspond to the named complex types.

    package forum11620825;
    
    import javax.xml.bind.annotation.*;
    
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "bookType", propOrder = {"author"})
    public class BookType {
    
        @XmlElement(required = true)
        protected String author;
    
        public String getAuthor() {
            return author;
        }
    
        public void setAuthor(String value) {
            this.author = value;
        }
    
    }
    

    ObjectFactory

    Global elements that correspond to named complex types have @XmlElementDecl annotations generated on the ObjectFactory class. This is necessary since multiple global elements could correspond to named complex types.

    package forum11620825;
    
    import javax.xml.bind.JAXBElement;
    import javax.xml.bind.annotation.XmlElementDecl;
    import javax.xml.bind.annotation.XmlRegistry;
    import javax.xml.namespace.QName;
    
    @XmlRegistry
    public class ObjectFactory {
    
        private final static QName _Book_QNAME = new QName("", "book");
    
        public ObjectFactory() {
        }
    
        public BooksList createBooksList() {
            return new BooksList();
        }
    
        public BookType createBookType() {
            return new BookType();
        }
    
        @XmlElementDecl(namespace = "", name = "book")
        public JAXBElement<BookType> createBook(BookType value) {
            return new JAXBElement<BookType>(_Book_QNAME, BookType.class, null, value);
        }
    
    }
    

    XML

    Below are the XML documents from your question.

    booksList.xml

    <booksList>
        <book>
            <author>XYZ</author>
        </book>
    </booksList>
    

    book.xml

    <book>
        <author>XYZ</author>
    </book>
    

    DEMO CODE

    When you unmarshal a document in which the root element corresponds to an @XmlRootElement annotation you get an instance of the corresponding domain object. If you unmarshal a document in which the root element corresponds to an @XmlElementDecl annotation you get back an instance of JAXBElement that wraps a domain object corresponding to the named complex type.

    package forum11620825;
    
    import java.io.File;
    import javax.xml.bind.*;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            JAXBContext jc = JAXBContext.newInstance("forum11620825");
            Unmarshaller unmarshaller = jc.createUnmarshaller();
    
            File input1 = new File("src/forum11620825/booksList.xml");
            BooksList bookList = (BooksList) unmarshaller.unmarshal(input1);
    
            File input2 = new File("src/forum11620825/book.xml");
            JAXBElement<BookType> je = (JAXBElement<BookType>) unmarshaller.unmarshal(input2);
            BookType bookType = je.getValue();
        }
    
    }
    

    UPDATE

    Below is a code fragment demonstrating how to wrap an instance of BookType in a JAXBElement so that it can be marshalled.

    ObjectFactory objectFactory = new ObjectFactory();
    JAXBElement<BookType> jaxbElement = objectFactory.createBook(aBookType);
    marshaller.marshal(jaxbElement, System.out);