Reputation: 4196
I've found another difference between handling Jaxb on Java 7 versus Java 8. I've reduced the issue to a simplified example, and the code should run as a single class. (changed the categories so it's non-work-related etc) When the setter for a List is called by the Unmarshaller: My Question is really a variation of
When run in Java 7 the setter will be called with data in the List. When run in Java 8 the setter will be called only with an empty List object which apparently gets populated later in the unmarshalling process. The difference I experience is that I must not have the setter do any processing on the List, but rather have a process that is invoked only after the overall object is unmarshalled. Or to sum it up, "don't do any processing in the setter". Example is below: (Three classes) - Under Java 7 the result returned is the first "album" title on the list as found in the setter. Under Java 8 a null is returned. The code should run as a single class with no dependencies. If run in Java 7 "First Album" title displayed is "Abbey Road". If run in Java 8 "First Album" title is null
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
public class MainJaxbCase {
public static void main( String[] args ) {
new MainJaxbCase().testIt();
}
private void testIt() {
try {
AlbumLib myLib = (AlbumLib) loadJaxbDocFromString( inXmlStr, AlbumLib.class );
System.out.println("category:"+ myLib.getCateg());
List<AlbumItm> albumList = myLib.getAlbumList();
System.out.println("AlbumList size is " + albumList.size());
System.out.println("The first album is titled:"
+ myLib.getFirstAlbumTitle()
+ "- shows \"null\" if using Java 8, \"Abbey Road\" if using Java 7"
);
} catch ( Exception e ) {
System.out.println( e.getClass().getSimpleName() + ", msg:" + e.getMessage() );
e.printStackTrace();
}
}
private final String inXmlStr =
"<my_lib categ='albums'>"
+ " <album title='Abbey Road'/> "
+ " <album title='Revolver'/>"
+ " <album title='Sgt.Pepper'/>"
+ "</my_lib>";
private Object loadJaxbDocFromString ( String inStr, Class<?> clazz ) throws Exception {
Object result = null;
try {
InputStream is = new ByteArrayInputStream( inStr.getBytes() );
result = unmarshal( is, clazz );
} catch ( Exception e ) {
String msg = this.getClass().getSimpleName() + ".loadJaxbDocFromResource() caught " + e.getClass().getSimpleName() + " msg:" + e.getMessage();
throw new Exception(msg);
}
return result;
}
private Object unmarshal( InputStream prmIs, Class<?> clazz ) throws Exception{
Object obj = null;
try {
JAXBContext jaxbContext = JAXBContext.newInstance( clazz );
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
obj = jaxbUnmarshaller.unmarshal( prmIs );
} catch ( Exception e ) {
String msg = this.getClass().getSimpleName() + " caught " +
e.getClass().getSimpleName() + ", msg:" + e.getMessage();
msg += " Trying to Unmarshall class " + clazz.getName();
System.err.println(msg);
e.printStackTrace();
throw new Exception(msg);
}
return obj;
}
}
@XmlRootElement ( name= "my_lib")
class AlbumLib {
private String categ;
private List<AlbumItm> albumList;
private String firstAlbumTitle;
@XmlAttribute ( name="categ")
public String getCateg() {
return this.categ;
}
public void setCateg( String val ) {
this.categ=val;
}
@XmlElement ( name="album")
public List<AlbumItm> getAlbumList() {
return this.albumList;
}
public void setAlbumList( List<AlbumItm> newList ) {
if ( newList != null && newList.size() > 0 ) {
firstAlbumTitle = newList.get(0).getTitle();
}
this.albumList = newList;
}
public String getFirstAlbumTitle() {
return this.firstAlbumTitle;
}
}
@XmlType(name = "album")
class AlbumItm {
private String title;
@XmlAttribute ( name="title" )
public String getTitle() {
return this.title;
}
public void setTitle(String val ) {
this.title = val;
}
}
This question arose because we had code that started behaving with subtle (but important) differences when it was switched to Java 8, but without any obvious exceptions.
Upvotes: 2
Views: 4137
Reputation: 6148
We met the same problem with you, as this blog post stated.
- Is the recommended practice for coding List accessors to omit the Setter altogether when coding JaxB ? ( since it seems to do handle the List via the Getter )
- Are there recommended alternative approaches?
The point is not to 'omit the Setter' but not to add more functionality to setter. Setter is just setter, nothing more. Adding more logic violate the 'single responsibility rule' for setter and the name of this function diff from what it actually done, where more bugs may arise.
When it comes the recommended ways, there is no standard answer that covers all cases. Considering there exist no constructor init (which is somewhat hard for JAXB to do) and we need more logic after setter, we have two directions which you can take:
XmlAdapter
to customize the construction to add logic: adapter examples;Upvotes: 2