Jackson mapper with polymorphimsm - java

I have classes in following structure :
class Member {
public long id;
public String name;
public String type;
public Pet pet;
};
public abstract class Pet {
}
public CatPet extends Pet {
public int age;
public String color;
}
public DogPet extends Pet {
public int age;
public String breed;
}
I have to serialize the objects of class Member into JSON string and vice-versa. Can I somehow make the serialization such that serialization of object Pet in class Member will be dependent on member 'type'. If type = "cat" it should serialize/deserialize using class CatPet.

Yes, you typically use annotation #JsonTypeInfo on base class, to indicate how polymorphic type information is to be used. And with that, things will "just work".

Related

Using class as extend of the Entity, but it is not the physical table in the model

I am not sure if title of the post is understandable, but I will try to explain it with examples.
The main class is the entity, lets name it Animal
#Entity
public class Animal {
#Id
private Long id;
private String param1;
}
The param1 is some basic property which only extended classes will expose what it represents:
public class Cat extends Animal {
public String getName() {
return super.getParam1();
}
public void setName(String name) {
super.setParam1(name);
}
}
public class Dog extends Animal {
public String getBreed() {
return super.getParam1();
}
public void setBreed(String breed) {
super.setParam1(breed);
}
}
When creating a new object of Cat and trying to save it, I get the error Unknown entity: com.example.Cat.
I have read about persistence inheritance but I think this is not the case since mine extended classes are just some logic in the services and not the actual tables in the model.
Is there any solution on how I can save the superclass ?

Dozer: Mapping fields for specific subclasses

The situation is this:
On the side of the domain we have a superclass let's call it Plant
and two subclasses Vegestable and DecorativePlant.
public abstract class Plant {
private String name;
#Mapping("elementValue.carbonValue")
private int carbonValue;
#Mapping("elementValue.oxygenValue")
private int oxygenValue;
}
public abstract class Vegestable extends Plant {
private int nutritionValue;
}
public abstract class DecorativePlant extends Plant {
private int rating;
}
now on the side of our soap api we have simular objects. The main difference
would probably be that we don't want the decorative plant on the soap-side to have
public class Vegestable {
private int nutrition;
private ElementValue elementValue;
}
public class DecorativePlant {
private int rating;
}
public class ElementValue {
private int carbonValue;
private int oxygenValue;
}
So now I was wondering if it is possible to specify that Dozer only maps the fields carbonValue and oxygenValue for subclasses of Vegestable and not for subclasses of DecorativePlant? If it's possible in Dozer than I won't have to actually alter my classes on the domain level and basically place the carbonValue and oxygenValue in both subclasses and than Dozer won't do the mapping for DecorativePlant. (A side from the #Mapping annotation I'm doing all my mapping in a mappings.xml file.)
Big thanks in advance!

How to pass a variable from 2 different POJOs?

I got 2 POJOs that are being passed to a HttpEntity and converted to json.
Before passing them i need a variable which is in both of the POJOs with different names because of the needs of the API so i cant change them.
What is the best way without casting and in terms of OOP also within the POJO definition like mentioned in Wikipedia?
Abstract pojo
public abstract class Pojo{
//some common variables
//setter getters
}
PojoOne
public class PojoOne extends Pojo{
private String id;
//setter getter for id
}
PojoTwo
public class PojoTwo extends Pojo{
private String identifier;
// setter getter for identifier
}
Class that
public class SomeOtherClass {
public void getIdForUse(Pojo pojo){
String s = pojo. // How should this be to have ability to get both id and identifier
}
}
Add a common method to the common superclass:
public abstract class Pojo {
public abstract String getId();
// some common variables
// setter getters
}
public class PojoOne extends Pojo {
private String id;
#Override
public String getId() {
return id;
}
//setter for id
}
public class PojoTwo extends Pojo {
private String identifier;
// setter getter for identifier
#Override
public String getId() {
return identifier;
}
}

Mapping abstract class in dozer

I have the following class structure (it actually is a VO layer with Hibernate mappings):
public abstract class abstractClassVO {
private int id;
private String name;
}
public class concreteClassAVO extends abstractClassVO {
private String aAttribute;
}
public class concreteClassBVO extends abstractClassVO {
private Long bAttribute;
}
And the equivalent DTO objects:
public abstract class abstractClassDTO {
private int id;
private String name;
}
public class concreteClassADTO extends abstractClassDTO {
private String aAttribute;
}
public class concreteClassBDTO extends abstractClassDTO {
private Long bAttribute;
}
Then I have another object like this:
public class compositeObject {
private int anAttribute;
private abstractClassVO myInstance;
}
and its equivalent:
public class compositeObjectDTO{
private int anAttribute;
private abstractClassDTO myInstance;
}
How can I tell dozer to automatically map myInstance to the specific DTO that corresponds to the concrete class implementation in the VO layer?
Currently, out of the box, Dozer isn't even putting anything in the myInstance field of the compositeObjectDTO class. My guess is that it's due to the fact that abstractClassDTO it is an abstact class, and since it cannot determine the implementation, it does nothing. I am not getting any exceptions.
Dozer can't do it out of the box but you could write a helper that would determine destination class by source class. You can get this information from DozerBeanMapper.getMappingMetadata().getClassMappings* methods. These methods return list of ClassMappingMetadata that contains destination class. You just only need to chech whether destination class is inherited from abstractClassDTO. This check can be omitted if you only have one mapping for one VO.
For bi-directional mapping you should additionally check ClassMappingMetadata.MappingDirection field.

How to deserialize JSON Array contained an abstract class without modifying a parent class?

I'm trying to deserialize JSON Array, which is persisted into my MongoDB, to a Java object by using Jackson. I found many tutorials mentioned to handle this polymorphism by adding:
#JsonTypeInfo(use=Id.CLASS,property="_class")
to a Super-class. However, in my case, I can't be able to modify the Super-class. So, are there some solutions to solve it without modifying the Super-class? Here is my code:
public class User {
#JsonProperty("_id")
private String id;
private List<Identity> identities; // <-- My List contains objects of an abstract class; Identity
public User(){
identities = new ArrayList<Identity>();
}
public static Iterable<User> findAllUsers(){
return users().find().as(User.class); // Always give me the errors
}
/*More code*/
}
It always give me the error - Can not construct instance of securesocial.core.Identity, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information.
You can use #JsonDeserilize annotation to bind a concrete implementation class to an abstract class. If you cannot modify your abstract class you can use the Jackson Mix-in annotations to tell Jackson how to find the implementation class.
Here is an example:
public class JacksonAbstract {
public static class User {
private final String id;
private final List<Identity> identities;
#JsonCreator
public User(#JsonProperty("_id") String id, #JsonProperty("identities") List<Identity> identities) {
this.id = id;
this.identities = identities;
}
#JsonProperty("_id")
public String getId() {
return id;
}
public List<Identity> getIdentities() {
return identities;
}
}
public static abstract class Identity {
public abstract String getField();
}
#JsonDeserialize(as = IdentityImpl.class)
public static abstract class IdentityMixIn {
}
public static class IdentityImpl extends Identity {
private final String field;
public IdentityImpl(#JsonProperty("field") String field) {
this.field = field;
}
#Override
public String getField() {
return field;
}
}
public static void main(String[] args) throws IOException {
User u = new User("myId", Collections.<Identity>singletonList(new IdentityImpl("myField")));
ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(Identity.class, IdentityMixIn.class);
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(u);
System.out.println(json);
System.out.println(mapper.readValue(json, User.class));
}
}

Categories