Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can JAXB generate ArrayList instead of List?

<complexType name="BookShelf">
   <sequence>
      <choice minOccurs="0" maxOccurs="unbounded">
         <element name="newBook" type="string"/>
         <element name="oldBook" type="string"/>
      </choice>
   </sequence>
</complexType>

JAXB generates the property as a List<JAXBElement<String>>. Is there any way it can be generated as ArrayList?

like image 345
Raj Avatar asked Nov 30 '22 09:11

Raj


2 Answers

Why, what good would that do you?

  1. ArrayList<E> has no public methods that are not in the List<E> interface, so there is nothing you could do with the ArrayList<E> that you couldn't do with any other List<E> (actually there is one: ArrayList.trimToSize(), thanks @Joachim Sauer, but it's hardly ever needed).
  2. It's awful practice for an API to accept or return implementation types instead of the underlying interfaces. I'd suggest you to follow the Collections Trail of the Sun Java Tutorial and / or read Effective Java by Joshua Bloch (you'll get an idea of what he's talking about from this short preview, which is the source of the quote below) to learn more about the Collections framework and interface usage.
  3. Who says the underlying List implementation isn't ArrayList? ArrayList is the most commonly-used List implementation anyway, so chances are high that JAXB will actually return an ArrayList, it just won't tell you so (because you don't need to know).

Item 52: Refer to Objects by their Interfaces (excerpt)

Item 40 contains the advice that you should use interfaces rather than classes as parameter types. More generally, you should favor the use of interfaces rather than classes to refer to objects. If appropriate interface types exist, then parameters, return values, variables, and fields should all be declared using interface types. The only time you really need to refer to an object’s class is when you’re creating it with a constructor. To make this concrete, consider the case of Vector, which is an implementation of the List interface. Get in the habit of typing this:

// Good - uses interface as type
List<Subscriber> subscribers = new Vector<Subscriber>();

rather than this:

// Bad - uses class as type!
Vector<Subscriber> subscribers = new Vector<Subscriber>();

[ ... ]

Source: Effective Java, preview on SafariBooksOnline.

like image 105
Sean Patrick Floyd Avatar answered Dec 04 '22 11:12

Sean Patrick Floyd


By default the property will be a List and the underlying implementation will be an ArrayList. Of course you can use JAXB customizations to change the underlying implementation, or use your own class with a property of type ArrayList (although for the reasons mentioned in other answers this is rarely a good idea).

Default JAXB Generation

Given your XML Schema:

<schema xmlns="http://www.w3.org/2001/XMLSchema">
   <complexType name="BookShelf">
      <sequence>
         <choice minOccurs="0" maxOccurs="unbounded">
            <element name="newBook" type="string"/>
            <element name="oldBook" type="string"/>
         </choice>
      </sequence>
   </complexType>
</schema>

Using the following command line:

xjc -d out your-schema.xsd

JAXB will generate the following class:

package generated;

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlType;


@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "BookShelf", propOrder = {
    "newBookOrOldBook"
})
public class BookShelf {

    @XmlElementRefs({
        @XmlElementRef(name = "newBook", type = JAXBElement.class),
        @XmlElementRef(name = "oldBook", type = JAXBElement.class)
    })
    protected List<JAXBElement<String>> newBookOrOldBook;

    public List<JAXBElement<String>> getNewBookOrOldBook() {
        if (newBookOrOldBook == null) {
            newBookOrOldBook = new ArrayList<JAXBElement<String>>();
        }
        return this.newBookOrOldBook;
    }

}

Customizing the Generation

By default JAXB will have the property type be List with the underlying implementation being ArrayList. If you wish to control the underlying implementation you can use an external binding file like:

<jxb:bindings 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    version="2.1">

    <jxb:bindings schemaLocation="f3.xsd">
            <jxb:bindings node="//xs:complexType[@name='BookShelf']/xs:sequence/xs:choice">
                <jxb:property collectionType="java.util.LinkedList"/>
            </jxb:bindings>
    </jxb:bindings>

</jxb:bindings>

And the following XJC call:

xjc -d out -b binding.xml your-schema.xsd

To get the following class instead:

package generated;

import java.util.LinkedList;
import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlType;


@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "BookShelf", propOrder = {
    "newBookOrOldBook"
})
public class BookShelf {

    @XmlElementRefs({
        @XmlElementRef(name = "oldBook", type = JAXBElement.class),
        @XmlElementRef(name = "newBook", type = JAXBElement.class)
    })
    protected List<JAXBElement<String>> newBookOrOldBook = new LinkedList<JAXBElement<String>>();

    public List<JAXBElement<String>> getNewBookOrOldBook() {
        if (newBookOrOldBook == null) {
            newBookOrOldBook = new LinkedList<JAXBElement<String>>();
        }
        return this.newBookOrOldBook;
    }

}

Using your own class:

You can also use your own class with a property of type ArrayList (although for the reasons mentioned in other answers this is rarely a good idea).

package com.example;

import java.util.ArrayList;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "BookShelf", propOrder = {
    "newBookOrOldBook"
})
public class BookShelf {

    @XmlElementRefs({
        @XmlElementRef(name = "oldBook", type = JAXBElement.class),
        @XmlElementRef(name = "newBook", type = JAXBElement.class)
    })
    protected ArrayList<JAXBElement<String>> newBookOrOldBook ;

    public ArrayList<JAXBElement<String>> getNewBookOrOldBook() {
        if (newBookOrOldBook == null) {
            newBookOrOldBook = new ArrayList<JAXBElement<String>>();
        }
        return this.newBookOrOldBook;
    }

}

For More Information:

  • http://bdoughan.blogspot.com/2011/01/jaxb-and-choosing-list-implementation.htm
like image 41
bdoughan Avatar answered Dec 04 '22 12:12

bdoughan