How can I use Jackson mapping for java.io.Serializable fields?
I have a situation where a POJO extends an abstract superclass that defines methods of type getId()
and setId()
using type java.io.Serializable
(code is shown below). Whenever I deserialize the JSON string for my specific POJOs, I get the following exception:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.io.Serializable, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
at [Source: java.io.StringReader@6fd90825; line: 1, column: 2] (through reference chain: com.demo.jackson.AClass["id"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:716)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:140)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:525)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:99)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:242)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1270)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:897)
Java codes:
Abstract class Super
public abstract class AbstractClass {
protected abstract void setId(final Serializable id);
protected abstract Serializable getId();
}
Implementation class: AClass
public class AClass extends AbstractClass {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Serializable id) {
this.id = (Long) id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Implementation class: BClass
public class BClass extends AbstractClass {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(Serializable id) {
this.id = (String) id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Test class
public class JsonSerializerTest {
public static void main(String[] args) throws Exception {
final ObjectMapper objectMapper = new ObjectMapper();
serialize(objectMapper);
}
private static void serialize(final ObjectMapper objectMapper) throws Exception {
final String jsonString = "{\"id\":123,\"name\":\"AClass\"}";
final ObjectReader objectReader = objectMapper.reader(AClass.class);
final AClass a = objectReader.readValue(jsonString);
System.out.println(a);
}
}
Can anyone please provide some pointers?
~ NN
source to share
We had the same situation, we have to use java.io.Serializable as ID in entities. We have no problem with serialization, but in Deserialization we have a problem. In our application, we are using String in JOSN objects, so Stdandard's StringDeserializer does work - in theory, you could use it with any Serializable implementation. (Apache CXF and Jackson 2.4.1 are used):
Initialize the Jackson provider:
// Jackson JSON Provider
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new IdentifiableDeserializableModule());
JacksonJaxbJsonProvider jp = new JacksonJaxbJsonProvider(mapper, JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS);
jp.configure(SerializationFeature.INDENT_OUTPUT, indentJson);
And the module:
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.io.Serializable;
/**
* Jackson module to deserialize java.io.Serializable class.
*/
public class IdentifiableDeserializableModule extends SimpleModule {
public IdentifiableDeserializableModule() {
addDeserializer(Serializable.class, new StringDeserializer());
}
}
source to share
The solution that worked for us is shown below.
Abstract Class
: use Object
instead of Serializable
. I know what is Serializable
better for ID, but this problem was kind of a blocker for our application and we chose this solution.
public abstract class AbstractClass {
protected abstract void setId(final Object id);
protected abstract Object getId();
}
Implementation: AClass
public class AClass extends AbstractClass {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Object id) {
// We need this inverted way to get a Long from a String, but we didn't have any other option!
this.id = Long.valueOf(Objects.toString(id));
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Implementation: BClass
public class BClass extends AbstractClass {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(Object id) {
this.id = Objects.toString(id);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I don't know if this is an elegant solution or not (compromises (?) Serializable for objects), but please feel free to post a better one.
Thank.
~ NN
source to share
I recently ran into this situation. The solution I came across approaches a problem similar to that described by Nirinyan above, using Object
instead Serializable
. However, instead of using get / set methods with Object
or trying to create a separate custom de-serializer, I used a constructor to store the field type as Serializable
, allowing Jackson to provide an instance Object
.
I used annotation @JsonCreator
for the constructor and then separate annotations @JsonProperty
on the parameters (this is not required in Java 8 with the Parameter Naming Module makes this an even cleaner solution).
This answer also contains a wealth of information about @JsonCreator
.
For brevity, in the answer, the class defined below is immutable (all fields are final and no setters).
public class PersonLocation {
private final Serializable id;
private final String name;
private final Location location;
/**
* Constructor leveraged by Jackson to de-serialize an incoming
* PersonLocation instance represented in JSON.
*
* @param id The instance identity.
* @param name The name of the person.
* @param location The location of the person.
**/
@JsonCreator
public PersonLocation (
@JsonProperty("id") Object id,
@JsonProperty("name") String name,
@JsonProperty("location") Location location)
{
//This safety check and cast should be a static utility method.
if (!id instanceof Serializable) {
throw new IllegalArgumentException("Id must be a serializable type.");
}
this.id = (Serializable)id;
this.name = name;
this.location = location;
}
public Serializable getId () {
return id;
}
public String getName () {
return name;
}
public Location getLocation () {
return location;
}
}
source to share
I don't know what parameters you have, but what about overloading methods setId
in subclasses and then making Jackson ignore the ones that accept Serializable
?
So, it ClassA
will look like this:
public class AClass extends AbstractClass {
private Long id;
private String name;
public Long getId() {
return id;
}
@JsonIgnore
public void setId(Serializable id) {
this.id = (Long) id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
This solution is not very elegant, but it may work for you. If not, then I suggest checking out Jackson's custom serialization and / or polymorphic serialization .
source to share