18

Here is the use case:

@XmlRootElement
public class Book {
  public String title;
  public Book(String t) {
    this.title = t;
  }
}
@XmlRootElement
@XmlSeeAlso({Book.class})
public class Books extends ArrayList<Book> {
  public Books() {
    this.add(new Book("The Sign of the Four"));
  }
}

Then, I'm doing:

JAXBContext ctx = JAXBContext.newInstance(Books.class);
Marshaller msh = ctx.createMarshaller();
msh.marshal(new Books(), System.out);

This is what I see:

<?xml version="1.0"?>
<books/>

Where are my books? :)

yegor256
  • 102,010
  • 123
  • 446
  • 597
  • You can't marshall `ArrayList` (as other languages don't know ArrayList) but you can marshall `List`. – Buhake Sindi Nov 11 '10 at 08:55
  • The Elite Gentleman - I'm not sure what you mean. Unlike other XML binding and serialization libraries, JAXB does not include anything XML specific in the output. – bdoughan Nov 11 '10 at 11:02
  • Vincenzo - Do you need to have Books extend ArrayList? The common approach is for the Books class to have an ArrayList property. – bdoughan Nov 11 '10 at 11:08
  • @Blaise I'm using inheritance here (instead of Adapter pattern) because I want `Books` to extend `ArrayList`'s behavior, not to replace. – yegor256 Nov 11 '10 at 11:55
  • I found this question to be useful while I was investigation a solution to this problem. [http://stackoverflow.com/questions/25324218/jaxb-unmarshalling-xml-class-cast-exception]. I ended up going with a generic list to be more flexible, specifically the code linked at the beginning of that question. – Aaron L. Johnson Jun 02 '16 at 14:46

3 Answers3

18

The elements to be marshalled must be public, or have the XMLElement anotation. The ArrayList class and your class Books do not match any of these rules. You have to define a method to offer the Book values, and anotate it.

On your code, changing only your Books class adding a "self getter" method:

@XmlRootElement
@XmlSeeAlso({Book.class})
public class Books extends ArrayList<Book> {
  public Books() {
    this.add(new Book("The Sign of the Four"));
  }

  @XmlElement(name = "book")
  public List<Book> getBooks() {
    return this;
  }
}

when you run your marshalling code you'll get:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<books><book><title>The Sign of the Four</title></book></books>

(I added a line break for clarity's shake)

Tomas Narros
  • 13,390
  • 2
  • 40
  • 56
  • Yes, but it doesn't present a public attribute or an annotated method to get the values. So you'll have to wirte a new method an anotate it, or write a wrapper class. Try the code I proposed you. It will generate your expected XML. As you can see, I have keep your class level anotations (which are right), and added a getBooks (which return the same list instance) method with an @XMLElement anotation. – Tomas Narros Nov 11 '10 at 13:45
2

I don't think you can easily marshall a List as is. Consider using another class to wrap the list in. The following works:

@XmlType
class Book {
    public String title;

    public Book() {
    }

    public Book(String t) {
        this.title = t;
    }
}

@XmlType
class Books extends ArrayList<Book> {
    public Books() {
        this.add(new Book("The Sign of the Four"));
    }
}

@XmlRootElement(name = "books")
class Wrapper {
    public Books book = new Books();
}

Used like the following:

JAXBContext ctx = JAXBContext.newInstance(Wrapper.class);
Marshaller msh = ctx.createMarshaller();
msh.marshal(new Wrapper(), System.out);

it produces this result:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<books><book><title>The Sign of the Four</title></book></books>
musiKk
  • 14,751
  • 4
  • 55
  • 82
  • Well, this is not exactly what I'm expecting to get. Can you change you example to produce `...`? – yegor256 Nov 11 '10 at 13:12
  • @Vincenzo: I changed it accordingly. I see you already accepted the other answer. It is a bit shorter but in my opinion a bit strange. An XML document always has a single root element. Marshalling a `List` directly is a bit contrary to what I would expect. – musiKk Nov 11 '10 at 14:09
0

As @Blaise and @musiKk have pointed out, it would be better to simply have a List of Book in Books, and allow Books to be the true root element. I would not consider the extending of ArrayList an acceptable procedure in my own code.

Half_Duplex
  • 5,102
  • 5
  • 42
  • 58