Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transforming empty element into null when unmarshalling with JAXB

Tags:

java

jaxb

A class is defined with the following JAXB annotation:

class Course {
@XmlElement (name = "book")
List<Book> requiredBooks = new ArrayList<Book>();

When unmarshalling an XML document that contains this

<course>
  ...
  <book/>
</course>

I end up with a Book added to the list, with all of its attributes set to null. I don't control the XML input. How can I prevent this empty book from being added? I tried intercepting in set..() or add..() methods, but turns out JAXB bypasses setters when dealing with collections. Any suggestions?

like image 611
torngat Avatar asked Nov 15 '10 03:11

torngat


4 Answers

Well, I found one way to do it, although I don't find it super elegant.

Edit: Actually, the solution below doesn't work properly. It actually causes no entries to be added to the list, regardless of them being an empty element or fully formed with data.

Maybe I've done it wrong, so any comments are welcome.

I created an adapter to handle the conversion and informed JAXB using an annotation:

class Course {
@XmlElement (name = "book")
@XmlJavaTypeAdapter(BookListAdapter.class)
List<Book> requiredBooks = new ArrayList<Book>();

and then defined my adapter:

static class BookListAdapter extends XmlAdapter<Book[], List<Book>>
{
    public Book[] marshal(List<Book> aBookList)
    {
        if (aBookList!= null)
        {
            return aBookList.toArray(new Book[aBookList.size()]);
        }
        return null;
    }

    public List<Book> unmarshal(Book[] aBookArray)
    {
        List<Book> bookList = new ArrayList<Book>();
        if (aBookArray != null)
        {
            for (Book book : aBookArray)
            {
                if (StringUtils.isNotEmpty(book.getTitle()))
                {
                    bookList.add(book);
                }
            }
        }
        return bookList;
    }
}

It works as I want it to, but, as I said earlier, I'm not convinced of its elegance.
Edit: As described above, this doesn't work but probably due to some error on my part.

like image 111
torngat Avatar answered Nov 12 '22 19:11

torngat


In EclipseLink JAXB (MOXy) we have the concept of a null policy. This null policy gives you some flexibility on how to represent null.

You could modify your class to look like the following:

import org.eclipse.persistence.oxm.annotations.XmlMarshalNullRepresentation;
import org.eclipse.persistence.oxm.annotations.XmlNullPolicy;

@XmlRootElement
class Course {

    @XmlElement (name = "book")
    @XmlNullPolicy(
        nullRepresentationForXml = XmlMarshalNullRepresentation.EMPTY_NODE, 
        emptyNodeRepresentsNull=true)
    List<Book> requiredBooks = new ArrayList<Book>();
}

This informs MOXy JAXB that for this property you wish to treat empty elements as null for this property.

For this XML:

<course>
    <book/>
    <book>
        <title>Hello World</title>
    </book>
    <book/>
</course>

The requiredBooks property would unmarshal as: null, aBook, null.

There is currently a bug that is preventing this from working that we are addressing now. You can track the progress on this issue here:

  • https://bugs.eclipse.org/330402
like image 36
bdoughan Avatar answered Nov 12 '22 21:11

bdoughan


Try this,

class Course {
@XmlElement (name="book", required=false)
List<Book> requiredBooks = new ArrayList<Book>();
like image 1
Adeel Ansari Avatar answered Nov 12 '22 20:11

Adeel Ansari


I did a similar cludge:

public List<Photo> getPhotos() {
    removeUninitializedPhotos();
    return photos;
}

private void removeUninitializedPhotos() {
    List<Photo> photosToRemove = new ArrayList<Photo>();
    for (Photo photo : photos) {
        if (photo.getId() == null) {
            photosToRemove.add(photo);
        }
    }
    photos.removeAll(photosToRemove);
}

But I found it so horrible that I just ended up switching to Gson ;)

like image 1
matt burns Avatar answered Nov 12 '22 20:11

matt burns