Why does JAXB need a no arg constructor for marshalling? - java

If you try to marshal a class which references a complex type that does not have a no-arg constructor, such as:
import java.sql.Date;
#XmlRootElement(name = "Foo")
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
int i;
Date d; //java.sql.Date does not have a no-arg constructor
}
with the JAXB implementation that is part of Java, as follows:
Foo foo = new Foo();
JAXBContext jc = JAXBContext.newInstance(Foo.class);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(foo, baos);
JAXB will throw a
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions java.sql.Date does not have a no-arg default constructor
Now, I understand why JAXB needs a no-arg constructor on unmarshalling - because it needs to instantiate the object. But why does JAXB need a no-arg constructor while marshalling?
Also, another nit, why does Java's JAXB implementation throw an exception if the field is null, and isn't going to be marshalled anyway?
Am I missing something or are these just bad implementation choices in Java's JAXB implementation?

When a JAXB (JSR-222) implementation initializes its metadata it ensures that it can support both marshalling and unmarshalling.
For POJO classes that do not have a no-arg constructor you can use a type level XmlAdapter to handle it:
http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.html
java.sql.Date is not supported by default (although in EclipseLink JAXB (MOXy) it is). This can also be handled using an XmlAdapter specified via #XmlJavaTypeAdapter at field, property, or package level:
http://blog.bdoughan.com/2011/05/jaxb-and-joda-time-dates-and-times.html
http://blog.bdoughan.com/2011/01/jaxb-and-datetime-properties.html
Also, another nit, why does Java's JAXB implementation throw an
exception if the field is null, and isn't going to be marshalled
anyway?
What exception are you seeing? Normally when a field is null it is not included in the XML result, unless it is annotated with #XmlElement(nillable=true) in which case the element will include xsi:nil="true".
UPDATE
You could do the following:
SqlDateAdapter
Below is an XmlAdapter that will convert from the java.sql.Date that your JAXB implementation doesn't know how to handle to a java.util.Date which it does:
package forum9268074;
import javax.xml.bind.annotation.adapters.*;
public class SqlDateAdapter extends XmlAdapter<java.util.Date, java.sql.Date> {
#Override
public java.util.Date marshal(java.sql.Date sqlDate) throws Exception {
if(null == sqlDate) {
return null;
}
return new java.util.Date(sqlDate.getTime());
}
#Override
public java.sql.Date unmarshal(java.util.Date utilDate) throws Exception {
if(null == utilDate) {
return null;
}
return new java.sql.Date(utilDate.getTime());
}
}
Foo
The XmlAdapter is registered via the #XmlJavaTypeAdapter annotation:
package forum9268074;
import java.sql.Date;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement(name = "Foo")
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
int i;
#XmlJavaTypeAdapter(SqlDateAdapter.class)
Date d; //java.sql.Date does not have a no-arg constructor
}

To answer your question: I think this is just poor design in JAXB (or perhaps in JAXB implementations). The existence of a no-arg constructor gets validated during creation of the JAXBContext and therefore applies regardless if you want to use JAXB for marshalling or unmarshalling. It would have been great if JAXB would defer this type of check to JAXBContext.createUnmarshaller(). I think it would be interesting to dig into if this design is actually mandated by the spec or if it is an implementation design in JAXB-RI.
But there's indeed a workaround.
JAXB doesn't actually need a no-arg constructor for marshalling. In the following I'll assume you are using JAXB solely for marshalling, not unmarshalling. I also assume that you have control over the immutable object which you want to marshall so that you can change it. If this is not the case then the only way forward is XmlAdapter as described in other answers.
Suppose you have a class, Customer, which is an immutable object. Instantiation is via Builder Pattern or static methods.
public class Customer {
private final String firstName;
private final String lastName;
private Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// Object created via builder pattern
public static CustomerBuilder createBuilder() {
...
}
// getters here ...
}
True, that by default you cannot get JAXB to unmarshall such an object. You'll get error "....Customer does not have a no-arg default constructor".
There are at least two ways of solving this. They both rely on putting in a method or constructor solely to make JAXB's introspection happy.
Solution 1
In this method we tell JAXB that there's a static factory method it can use to instantiate an instance of the class. We know, but JAXB doesn't, that indeed this will never be used. The trick is the #XmlType annotation with factoryMethod parameter. Here's how:
#XmlType(factoryMethod="createInstanceJAXB")
public class Customer {
...
private static Customer createInstanceJAXB() { // makes JAXB happy, will never be invoked
return null; // ...therefore it doesn't matter what it returns
}
...
}
It doesn't matter if the method is private as in the example. JAXB will still accept it. Your IDE will flag the method as unused if you make it private, but I still prefer private.
Solution 2
In this solution we add a private no-arg constructor which just passes null into the real constructor.
public class Customer {
...
private Customer() { // makes JAXB happy, will never be invoked
this(null, null); // ...therefore it doesn't matter what it creates
}
...
}
It doesn't matter if the constructor is private as in the example. JAXB will still accept it.
Summary
Both solutions satisfies JAXB's desire for no-arg instantiation. It is a shame that we need to do this, when we know by ourselves that we only need to marshal, not unmarshal.
I have to admit that I do not know to what extent this is a hack that will only work with JAXB-RI and not for example with EclipseLink MOXy. It definitely works with JAXB-RI.

You seem to be under the impression that the JAXB introspection code will have action specific paths for initialization. if so, that would result in a lot of duplicate code and would be a poor implementation. i would imagine that the JAXB code has a common routine which examines a model class the first time it is needed and validates that it follows all the necessary conventions. in this situation, it is failing because one of the members does not have the required no-arg constructor. the initialization logic is most likely not marshall/unmarshall specific and also highly unlikely to take the current object instance into account.

Some enterprise and Dependency Injection frameworks use reflection Class.newInstance() to create a new instance of your classes. This method requires a public no-arg constructor to be able to instantiate the object.

Related

unexpected behavior from custom ObjectInputStream when reading object with LocalDate field

i'm using the example from this repo https://github.com/kojenov/serial/tree/master/3-4.%20upload, which is presenting a method for specifying a way to protect form unsafe deserialization in Java by defining a custom ObjectInputStream and overriding a protected method resolveClass in which we have to specify which classes are allowed for deserialisation.
My problem is I added a LocalDate field to the Planet class and when I deserialize a serialized object I get this exception:
invalid class except unsupported class; java.time.Ser
I searched online and I could not find any other encounter with that problem, so I'm really confused. I tried with instead of the LocalDate to add a LocalDateTime, the same error happens again. As far as I found that class java.time.Ser is a protected class somewhere in the hierarchy of the classes in that package.
The class LocalDate is serializable, so this should not happen. I know for sure that the problem is in the LocalDate, because if I make that field transient code works as intended. Am I missing something or it's just a bug of Java Object Serialization?
By the way, the examples are originally from a talk given by Alexei Kojenov, his site is kojenov.com, but i couldn't find his email to ask him personally.
Serialization is recursive progress, which means when you're serializing a complex object, firstly you need to serialize all its properties. The same thing happens with deserialization.
Planet object contains fields of type int, double and java.lang.String which are primitives and don't need special (de)serialization. LocalDate or LocalDateTime aren't primitives and they're serialized and then deserialized with SafeObjectInputStream.
Serialization hack
As it said in java.io.Serializable documentation, objects can modify their serialization behaviour and even delegate serialization to another class by defining method writeReplace.
JavaDoc cite:
Serializable classes that need to designate an alternative object to be used when writing an object to the stream should implement this special method with the exact signature:
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
This writeReplace method is invoked by serialization if the method exists and it would be accessible from a method defined within the class of the object being serialized. Thus, the method can have private, protected and package-private access. Subclass access to this method follows java accessibility rules.
Both LocalDate and LocalDateTime utilizes this possibility and define writeReplace method.
As an example, java.time.LocalDate's implementation:
private Object writeReplace() {
return new Ser(Ser.LOCAL_DATE_TYPE, this);
}
java.time.Ser is a package-private final class that is used as a delegate for java.time.* objects.
Hereby, when you're serializing java.time.LocalDate or java.time.LocalDateTime, actually java.time.Ser being serialized.
Custom deserializer
Previously we found out that java.time.LocalDate was serialized as java.time.Ser. Now, let's try to deserialize it with SafeObjectInputStream.
Before deserialization, resolveClass method is called:
#Override
protected Class<?> resolveClass(ObjectStreamClass input)
throws IOException, ClassNotFoundException
{
if (!input.getName().equals(Planet.class.getName())) {
throw new InvalidClassException("Unsupported class", input.getName());
}
return super.resolveClass(input);
}
It checks for class name to be equal to Planet.class.getName(), but java.time.Ser is not, that's why you're getting an exception.
Solution
To resolve this issue, you need to add java.time.Ser to the list of trusted classes. I would suggest modifying your SafeObjectInputStream next way:
public class SafeObjectInputStream extends ObjectInputStream {
private final List<String> supportedClasses = List.of(Planet.class.getName(), "java.time.Ser");
public SafeObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}
#Override
protected Class<?> resolveClass(ObjectStreamClass input)
throws IOException, ClassNotFoundException
{
if (!supportedClasses.contains(input.getName())) {
throw new InvalidClassException("Unsupported class ", input.getName());
}
return super.resolveClass(input);
}
}
NOTE: List.of was introduced in Java 9. If your Java version is less than 9, you can replace it with Arrays.asList.

How to get elements from autogenerated #XmlSeeAlso?

I'm generating java classes from wsdl using cxf / jaxb and the configuration <jaxb:globalBindings generateElementProperty="false">, which gives me the advantage to directly get the elements by getters and setters from the xml, not having to instantiate a JAXBElement explicit every time.
One element I want to access is autogenrated as #XmlSeeAlso.
Problem: the autogenerated class does not contain any getter for this class! How can I at all receive the properties inside there if there's no getter created??
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Fare", propOrder = {
"some",
"props"
})
#XmlSeeAlso({
AnySubType.class
})
#Generated
public class AutoGeneratedClass {
//there is no getAnySubType() method!
//only getters for some, props etc.
}
Where as:
#Generated
public class AnySubType
extends AutoGeneratedClass
{
I will attempt to address the points you raised:
//there is no getAnySubType() method!
There wouldn't be. It isn't common object-oriented practice to have a method on a super class that returns an instance of a specific subclass.
well somehow a few values that I need to get from the xml are inside
hte AnySubType, but a getter for that type is missing so I cannot
access them...
You would need to cast the instance of AutoGeneratedClass to AnySubType to access the subclass methods.

Magically call methods in Java

Is there some way of using magic methods in Java like there is in PHP with __call?
For instance:
class foo {
#Setter #Getter
int id;
#Getter
Map <String, ClassInFoo> myMap;
protected class ClassInFoo {
#Setter #Getter
String name;
}
#Setter
String defaultKey;
}
I'm using Project Lombok annotations for getter and setter methods to simplify the code.
Let's consider that that my map contains several items mapped by String and the defaultKey defines the default one.
What I would like is to be able to call foo.getName() which would return the default name as foo.myMap.get(defaultKey).getName().
The reason I can't just write all the getters manually is that the Foo class is in fact inherited with generics and the the inner class might be different.
I sort of need something like:
function Object __call(method) {
if (exist_method(this.method)
return this.method();
else
return this.myMap.get(defaultKey).method();
}
Is this somehow possible in Java?
EDIT:
I made a more precise example of what I am trying to achieve here: https://gist.github.com/1864457
The only reason of doing this is to "shorthand" the methods in the inner class.
You absolutely can through reflection by using its features like
public Method getMethod(String name, Class<?>... parameterTypes)
that can be used to see if a class has some methods defined but I don't see how your problem couldn't be solved with a proper use of interfaces, inheritance and overriding of methods
Features like reflection are provided to manage certain, otherwise unsolvable, issues but Java is not PHP so you should try to avoid using it when possible, since it's not in the philosophy of the language.
Isn't it the whole point of inheritance and overriding?
Base class:
public Object foo() {
return this.myMap.get(defaultKey).method();
}
Subclass:
#Overrides
public Object foo() {
return whateverIWant;
}

JavaBean class rules

What are the correct rules to write a JavaBean class?
I'm confused because some books use MUST while other user SHOULD or COULD to describe
the writing rule:
i.e.
a bean class MUST implements Serializable or SHOULD?
the instance variables MUST be private or SHOULD BE?
A JavaBean is defined by its properties (i.e. its getter and setter methods), not it's fields. Although the terms are used interchangably, that is actually not correct. The Introspector mechanism ignores fields completely.
Example
Take this (awfully designed) Javabean:
public class TestBean {
private int baz;
private char[] phleem;
public String getFoo() {
return new String(phleem);
}
public void setFoo(final String foo) {
this.phleem = foo.toCharArray();
}
public long getBar() {
return baz;
}
public void setBar(final long bar) {
this.baz = (int) bar;
}
}
You'd think the properties are:
"baz" (int)
"phleem" (char[])
but now let's inspect it with the Javabeans introspector:
for (PropertyDescriptor descriptor : Introspector
.getBeanInfo(TestBean.class, Object.class)
.getPropertyDescriptors()) {
System.out.println("Name: " + descriptor.getName() +
", type: " + descriptor.getPropertyType());
}
Here's the output:
Name: bar, type: long
Name: foo, type: class java.lang.String
Conclusion:
Getters and setters are what define a Javabeans property. It's a convention that they are backed by fields of the same name and type, but the fields are not actually part of the Javabean properties (although many documentations will suggest otherwise).
On re-reading my answer: it is meant as an addendum to the other answers. If you want a short and simple answer, go with skaffman's.
It is a public class.
It has a public parameterless constructor (though it may have other constructors
as well)
It implements Serializable interface (i.e. it can be made persistent, so its state can
be saved)
It has properties with “getter” and “setter” methods named by following
JavaBeans naming patterns
It has events which follow the standard Java event model with the registration
methods named by following the JavaBeans naming patterns
It may have other methods which do not follow the naming patterns. These
methods are not exposed by a builder tool.
Adding to the previous poster - skaffman. It is always a good practice to override, toString(), hashCode(), equals() and finally write a overloaded constructor that has all the fields (that this class has) as input.
Be sure not to use other references (like List, HashMaps etc) in the toString() and hashCode()'s implementation.
On a side note, eclipse has built-in functionality to generate them for you..
A Java Bean is a Java class that should follow the following conventions:
It should have a no-arg constructor.
It should be Serializable.
It should provide methods to set and get the values of the properties, known as getter and setter methods.
All the above and it should not cross the boundaries of Java API . It means it should not extend or implement any classes or interface,but one relaxation is there it can implement only one serializable interfce why because it is a marker interface

Jackson JSON library: how to instantiate a class that contains abstract fields

I want to convert a JSON string into java object, but the class of this object contains abstract fields, which Jackson can't instantiate, and doesn't produce the object. What is the easiest way to tell it about some default implementation of an abstract class, like
setDefault(AbstractAnimal.class, Cat.class);
or to decide about the implementation class based on JSON attribute name, eg. for JSON object:
{
...
cat: {...}
...
}
i would just wite:
setImpl("cat", Cat.class);
I know it's possible in Jackson to embed class information inside JSON, but I don't want to complicate the JSON format I use. I want to decide what class to use just by setting default implementation class, or by the attribute name ('cat') - like in XStream library, where you write:
xStream.alias("cat", Cat.class);
Is there a way to do so, especially in one line, or does it require some more code?
There are multiple ways; before version 1.8, simplest way is probably to do:
#JsonDeserialize(as=Cat.class)
public abstract class AbstractAnimal { ... }
as to deciding based on attribute, that is best done using #JsonTypeInfo, which does automatic embeddeding (when writing) and use of type information.
There are multiple kinds of type info (class name, logical type name), as well as inclusion mechanisms (as-included-property, as-wrapper-array, as-wrapper-object). This page: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization explains some of the concepts.
A full fledged answer with a very clear example can be found here: https://stackoverflow.com/a/30386694/584947
Jackson refers to this as Polymorphic Deserialization.
It definitely helped me with my issue. I had an abstract class that I was saving in a database and needed to unmarshal it to a concrete instance of a class (understandably).
It will show you how to properly annotate the parent abstract class and how to teach jackson how to pick among the available sub-class candidates at run-time when unmarshaling.
If you want to pollute neither your JSON with extra fields nor your classes with annotation, you can write a very simple module and deserializer that uses the default subclass you want. It is more than one line due to some boilerplate code, but it is still relatively simple.
class AnimalDeserializer extends StdDeserializer<Animal> {
public AnimalDeserializer() {
super(Animal.class);
}
public Animal deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
return jsonParser.readValueAs(Cat.class);
}
}
class AnimalModule extends SimpleModule {
{
addDeserializer(Animal.class, new AnimalDeserializer());
}
}
Then register this module for the ObjectMapper and that's it (Zoo is the container class that has an Animal field).
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new AnimalModule());
return objectMapper.readValue(json, Zoo.class);
The problem can be solved with the annotation #JsonDeserialize on the abstract class.
Refers to Jackson Exceptions Problems and Solutions for more info

Categories

Resources