2

Testdata.xml

<Users>
    <User>
        <Name>Ammu</Name>
        <Books>
            <Book>book1</Book>
            <Book>book2</Book>
            <Book>book3</Book>
        </Books>
    </User>
    <User>
        <Name>Unni</Name>
        <Books>
            <Book>book1</Book>
            <Book>book2</Book>
            <Book>book4</Book>
        </Books>
    </User>
</Users>

A call to an external service returns data in the above XML format, I am trying to design to convert this structure to a POJO so as to convert the XML to POJO using JAXB

Problem: Current output: books is returned as empty. Why?

com.example.Users@45db05b2[
  users=[com.example.User@2e530cf2[
  name=Ammu
  books=[]
], com.example.User@4e76fba0[
  name=Unni
  books=[]
]]
]

Book.java

import javax.xml.bind.annotation.XmlElement;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

public class Book {
    private String name;

    @XmlElement(name = "Book")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this,
                ToStringStyle.MULTI_LINE_STYLE);
    }
}

User.java

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

@XmlRootElement(name = "User")
public class User {

    private String name;

    private List<Book> books = new ArrayList<Book>();

    @XmlElement(name = "Book", type = Book.class)
    public List<Book> getBooks() {
        return books;
    }

    public void setBooks(List<Book> books) {
        this.books = books;
    }

    @XmlElement(name="Name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this,
                ToStringStyle.MULTI_LINE_STYLE);
    }
}

Users.java

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "Users")
public class Users {

    @XmlElement(name = "User", type = User.class)
    private List<User> users = new ArrayList<User>();

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this,
                ToStringStyle.MULTI_LINE_STYLE);
    }

}

Update:

After adding @XmlElementWrapper in User.java, there is great progress.

@XmlElementWrapper(name="Books")
    @XmlElement(name = "Book")
    public List<Book> getBooks() {
        return books;
    }

The current output is,

com.example.Users@39b8d6f7[
  users=[com.example.User@16290fbc[
  name=Ammu
  books=[com.example.Book@144aa0ce[
  name=<null>
], com.example.Book@2f833eca[
  name=<null>
], com.example.Book@518f5824[
  name=<null>
]]
], com.example.User@61c80b01[
  name=Unni
  books=[com.example.Book@19e3cd51[
  name=<null>
], com.example.Book@3abc8e1e[
  name=<null>
], com.example.Book@311671b2[
  name=<null>
]]
]]

But The values for <book> are null

Update2: Final

As per Blaise Doughan's update, tried adding @XmlValue, and that's it...

No need of any other annotation on Book.java, [and no need of Books.java , which I once thought would be required to declare just a list of Book] . cool

public class Book {
    private String name;

    @XmlValue
    public String getName() {
        return name;
    }

Output:

com.example.Users@3882764b[
  users=[com.example.User@7d2452e8[
  name=Ammu
  books=[com.example.Book@6860991f[
  name=book1
], com.example.Book@1de4f7c2[
  name=book2
], com.example.Book@2345f0e3[
  name=book3
]]
], com.example.User@5bbf3d87[
  name=Unni
  books=[com.example.Book@44c9d92c[
  name=book1
], com.example.Book@1fd0fafc[
  name=book2
], com.example.Book@510dc6b5[
  name=book4
]]
]]
]

This was slightly different usecase compared to those available as examples elsewhere. Thanks all esp Blaise Doughan.

Test kickoff

public static void main(String[] args) {
        try {
            File file = new File("C:\\temp\\testdata.xml");
            JAXBContext jaxbContext = JAXBContext.newInstance(Users.class);
            Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
            Users users = (Users) jaxbUnmarshaller.unmarshal(file);
            System.out.println(users);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
1
  • In User.java should not the annotation be @XmlElement(name = "Books", type = Book.class) (Books with an s) Commented Aug 19, 2014 at 19:34

2 Answers 2

3

You need to leverage the @XmlElementWrapper annotation for your use case:

@XmlElementWrapper(name="Books")
@XmlElement(name = "Book")
public List<Book> getBooks() {
    return books;
}

With the @XmlElementWrapper it will write the XML as:

<User>
    <Books>
        <Book>...</Book>
        <Book>...</Book>
    </Books>
</User>

without it, the expected XML is:

<User>
    <Book>...</Book>
    <Book>...</Book>
</User>

UPDATE

On the Book class you should annotate the name property with @XmlValue.

public class Book {
    private String name;

    @XmlValue
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you Blaise Doughan, @XmlElementWrapper was definitely the missing one +1. Now program is trying to pull the three <book> inside the books. but values are null. Something wrong in Book.java? I will post the current o/p in the question section as an update
@prash - See my update about using @XmlValue on the name property in the Book class.
That's it. Thanks a lot Blaise Doughan.
1

When using an array, for in you example of Users, you are mapping using @XmlRootElement(name = "Users"), but for Books there is no @XmlElement(name = "Books", type = Book.class), you are just mapping for "book".

Try putting:

@XmlElement(name = "Books", type = Book.class)

Instead of:

@XmlElement(name = "Book", type = Book.class)

1 Comment

I tried this, now the o/p structure slightly differs , ie it pulls 1 Book with empty name. com.example.Users@4e76fba0[ users=[com.example.User@6581ed9e[ name=Ammu books=[com.example.Book@e949f69[ name=<null> ]] ], com.example.User@35175422[ name=Unni books=[com.example.Book@3f6dadf9[ name=<null> ]] ]] ]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.