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

+3


source to share


4 answers


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());
    }
}

      

+3


source


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

+1


source


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;
    }
}

      

+1


source


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 .

0


source







All Articles