Leo
Leo

Reputation: 333

Spring MVC - receiving XML Objects and deserialzing into Collection

I'm attempting to deserialize an XML payload (body of a SOAP message, but nothing else), with a specific hierarchy of tags / objects. When attempting to aggregate unwrapped objects into a List, a MismatchedInputException is thrown.

Example Payload

<libraryRequest>
  <libraryProfile>
    <libraryId>
    <libraryName>
    <newBookInfo>
      <bookId>...</bookId>
      <bookTitle>...</bookTitle>
      <datePublished>...</datePublished>
    </newBookInfo>
    <currentBooks>
      <bookId>...</bookId>
      <bookTitle>...<bookTitle>
      <datePublished>...</datePublished>
    </currentBooks>
    <currentBooks>
      <bookId>...</bookId>
      <bookTitle>...<bookTitle>
      <datePublished>...</datePublished>
    </currentBooks>
    <currentBooks>...</currentBooks>
  </libraryProfile>
</libraryRequest>

Java objects are

public class LibraryRequest {
    private LibraryProfile libraryProfile;
    @XmlElement(name = "libraryProfile")
    public LibraryProfile getLibraryProfile(){
    ...
    }
// setters

public class LibraryProfile {
    // constructors, getters & setters for primitive types 

    private List<BookInfo> bookInfos;
    public List<BookInfo> getBookInfo(){
        return this.BookInfos;
    }
    // rest of the functions

My issue is that I don't know how many currentBooks tags will come in the XML payload, and they don't come in a wrapper element. I need to keep track of each currentBook element, which is why I was using a Collection, but I am not able to properly fill the collection with the information contained within the currentBooks tags.

Would I be able to use JAXB to group the XML sequence into a Java Collection/List, and if not would I be able to use Jackson's XML functionality to group the unwrapped XML tags into a Java Collection?

The main goal is to use have an XML request come into a Spring Controller and have the XML sequence properly deserialized into a Java List / Collection. Any advice would help.

I'm using Spring Boot 1.5.8 (later version was giving me trouble in a different way), and Jackson version 2.9.5

Upvotes: 1

Views: 73

Answers (1)

Johannes Leimer
Johannes Leimer

Reputation: 1339

This is based on the XmlElement explanation from actimem.com.

The mechanics explained: - @XmlElement is only needed if the field name is not equal to the xml tag name. - If you would like to rename your field newBookInfo to newestBook but without changing the xml you'd simply rename your field and annotate it with @XmlElement(name="newBookInfo") - @XmlElementWrapper is explicitly not used to advice JAXB it should search for the list tags directly in the parent node

The XML represenation classes Book

@XmlAccessorType(XmlAccessType.FIELD)
public static class Book {
    private String bookId;
    private String bookTitle;
    // ... accessors and toString 
}

and LibraryProfile

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static class LibraryProfile {
    private String libraryId;
    private String libraryName;
    private Book newBookInfo;
    // the trick is NOT to use @XmlElementWrapper here
    private List<Book> currentBooks;
    private String foobar; // just to show a tag after the list 
    // ... accessors
}

The input based on your question (I skipped the <libraryRequest> to keep the example short)

  <libraryProfile>
    <libraryId>1</libraryId>
    <libraryName>library of alexandria</libraryName>
    <newBookInfo>
      <bookId>42</bookId>
      <bookTitle>the answer</bookTitle>
    </newBookInfo>
    <currentBooks>
      <bookId>2</bookId>
      <bookTitle>the second</bookTitle>
    </currentBooks>
    <currentBooks>
      <bookId>1</bookId>
      <bookTitle>the first</bookTitle>
    </currentBooks>
    <foobar>test-foo</foobar>
  </libraryProfile>

And here the testing class:

package com.stackoverflow.answer;

import javax.xml.bind.JAXB;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.StringReader;

public class Tester {

    public static final String INPUT = "..."; // here goes your xml

    public static void main(String[] args) throws Exception {
        LibraryProfile lib = JAXB.unmarshal(new StringReader(INPUT), LibraryProfile.class);
        System.out.println(lib.getLibraryName() + " currently contains");
        System.out.println(lib.getCurrentBooks());
        System.out.println("the newest book is: " + lib.getNewBookInfo());
    }
}

The output is now

library of alexandria currently contains
[Book{bookId='2', bookTitle='the second'}, Book{bookId='1', bookTitle='the first'}]
the newest book is: Book{bookId='42', bookTitle='the answer'}

Upvotes: 3

Related Questions