Can JAXB generate an 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 a property as List<JAXBElement<String>> . Is there a way that it can be generated as an ArrayList?

+4
source share
3 answers

Why, what would you do?

  • ArrayList<E> does not have public access methods that are not in List<E> , so there is nothing you could do with ArrayList<E> , which you could not do with any other List<E> (actually there is one : ArrayList.trimToSize() , thanks @Joachim Sauer, but this is hardly ever needed).
  • It is a terrible practice for an API to accept or return an implementation of types instead of interfaces. I would suggest that you follow the Trail Sun Java Tutorial Collections and / or read Effective Java from Joshua Bloch (you will understand what he is talking about from this short Preview , which is the source of the quote below) to learn more about Collection Structure and Interface Usage.
  • Who says the list implementation is not an ArrayList ? ArrayList is the most commonly used implementation of List anyway, so the chances are high that JAXB will actually return ArrayList , it just won't tell you that (because you don't need to know).

Item 52: refer to objects by their interfaces (excerpt)

Clause 40 provides advice that you should use interfaces, not classes, as parameter types. More generally, you should approve the use of interfaces, not classes, refer to objects. If interface types exist, parameters, return values, variables, and all fields must be declared using interface types. The only time you really need to refer to the class of objects when you create this with a constructor. To make this specific, consider the Vector case, which is an implementation of the List interface. Get into the habit of typing this:

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

instead of this:

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

[...]

Source: Effective Java, preview in SafariBooksOnline .

+14
source

By default, the property will be List, and the base implementation will be ArrayList. Of course, you can use JAXB settings to change the underlying implementation, or use your own class with a property like 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; } } 

Generation Setup

By default, JAXB will have a be List property type with the underlying implementation being an ArrayList. If you want to control the underlying implementation, you can use an external binding file, for example:

 <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 

Instead, you get the following class:

 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; } } 

Additional Information:

+5
source

You cannot change the fact that the API creates a list.

However, assuming the base implementation actually creates an ArrayList, you can always just pass it to an ArrayList:

 ArrayList<JAXBElement<String>> arrayList = (ArrayList<JAXBElement<String>>) list; 

Or, if it is not an arraylist (i.e. you get an exception trying to fulfill the above ...), you can create a new ArrayList containing the same list items.

 ArrayList<JAXBElement<String>> arrayList = new ArrayList<JAXBElement<String>>(list); 

In general, however, you do not need to do anything: it is always better to code for an abstraction of an interface, rather than a base concrete class whenever you can.

+1
source

Source: https://habr.com/ru/post/1336915/


All Articles