Json Jackson - Using Alias for field names [duplicate] - java

I use different NoSQL databases and depending on the database I need to name the "id" different. So for example in OrientDB the id is named "#rid"
#JsonProperty("#rid")
private String id;
And for MongoDB the id is named "_id"
#JsonProperty("#_id")
private String id;
I do not know what is wrong with the modern DB developers not just naming the id field "id" ^^. But now I have a problem. How can I dynamically serialize/deserialize the id field in some case as "#rid" and in another case as "_id"?
EDIT:
Based on rmullers suggestion I have tried to use mixins. So I have for example:
public interface IdMixins {
}
public interface MongoIdMixIn extends IdMixins {
#JsonProperty("_id") String getId();
#JsonProperty("_id") void setId(String id);
}
public interface OrientIdMixIn extends IdMixins{
#JsonProperty("#rid") String getId();
#JsonProperty("#rid") void setId(String id);
}
Where IdMixins is a completly empty interface just used to get more controll which interfaces can be passet to the mapper.
Then there is a class:
#JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="#javaClass")
public abstract class AbstractBean implements Serializable {
private static final long serialVersionUID = -1286900676713424199L;
// #JsonProperty("#rid")
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
But when I run this simple test, the output is still "id":
public class MixinTest {
public static void main(String[] args) throws JsonProcessingException {
Foo f = new Foo();
f.setId("123");
f.setBar("lala");
ObjectMapper mapper = new ObjectMapper();
ObjectMapper m2 = mapper.copy();
m2.addMixInAnnotations(AbstractBean.class, MongoIdMixIn.class);
System.out.println(m2.writeValueAsString(f));
ObjectMapper m3 = mapper.copy();
m3.addMixInAnnotations(AbstractBean.class, OrientIdMixIn.class);
System.out.println(m3.writeValueAsString(f));
}
public static class Foo extends AbstractBean {
private String bar;
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
}
Outputs:
{"#javaClass":"test.MixinTest$Foo","id":"123","bar":"lala","#class":"Foo"}
{"#javaClass":"test.MixinTest$Foo","id":"123","bar":"lala","#class":"Foo"}

Have you tried using http://wiki.fasterxml.com/JacksonMixInAnnotations? Then you can use an OrientDbMixin and a MongoDbMixin with different #JsonProperty configuration.
Update: Working example
public final class JacksonTest {
static final class ExampleBean {
private String id;
private String bar;
#JsonProperty("donotwanttoseethis")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
public interface MongoIdMixIn {
#JsonProperty("_id") String getId();
}
public interface OrientIdMixIn {
#JsonProperty("#rid") String getId();
}
private final static Logger LOG = LoggerFactory.getLogger();
public static void main(String[] args) throws JsonProcessingException {
ExampleBean bean = new ExampleBean();
bean.setId("1234");
bean.setBar("lala");
ObjectMapper m2 = new ObjectMapper();
m2.addMixInAnnotations(ExampleBean.class, MongoIdMixIn.class);
LOG.info(m2.writeValueAsString(bean));
ObjectMapper m3 = new ObjectMapper();
m3.addMixInAnnotations(ExampleBean.class, OrientIdMixIn.class);
LOG.info(m3.writeValueAsString(bean));
}
}

Related

Serialize/Deserialize a java.io.Serializable object but missing type id property

I try to serialize and deserialize an object DataSourceObject that wraps a Serializable object, but I don't know the type of the wrapped object.
When I deserialize the JSON, I get an exception:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.accor.assets.TestSerialization$DataSourceObject` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"#class":"com.accor.assets.TestSerialization$DataSourceObject","concreteObject":{"#class":"com.accor.assets.TestSerialization$HotelsListBean","version":"1.0","metaResponse":{"#class":"com.accor.assets.TestSerialization$MetaResponse","returncode":"0","date":"10/28/21 09:39:14 AM"},"hotelsList":{"#class":"com.accor.assets.TestSerialization$HotelsList","hotel":["java.util.ArrayList",[{"#class":"com.accor.assets.TestSerialization$Hotel","name":"My Hotel","code":"H001","nbstars":"4","countryCode":"G"[truncated 8 chars]; line: 1, column: 65]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1764)
at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1209)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1415)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:362)
Here is a complete example to reproduce the problem:
public class TestSerialization {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = objectMapper();
HotelsListBean hotelsListBean = new HotelsListBean();
hotelsListBean.setVersion("1.0");
MetaResponse metaResponse = new MetaResponse();
metaResponse.setReturncode("0");
metaResponse.setDate("10/28/21 09:39:14 AM");
hotelsListBean.setMetaResponse(metaResponse);
HotelsList hotelsList = new HotelsList();
Hotel hotel = new Hotel();
hotel.setCode("H001");
hotel.setName("My Hotel");
hotel.setCountryCode("GB");
hotel.setNbstars("4");
hotelsList.getHotel().add(hotel);
hotelsListBean.setHotelsList(hotelsList);
DataSourceObject<HotelsListBean> dataSourceObject = new DataSourceObject<>(hotelsListBean);
String json = objectMapper.writeValueAsString(dataSourceObject);
System.out.println(json);
Object result = objectMapper.readValue(json, Object.class);
System.out.println(result);
}
private static ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(JsonGenerator.Feature.IGNORE_UNKNOWN);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
// Register support of other new Java 8 datatypes outside of date/time: most notably Optional, OptionalLong, OptionalDouble
objectMapper.registerModule(new Jdk8Module());
// Register support for Java 8 date/time types (specified in JSR-310 specification)
objectMapper.registerModule(new JavaTimeModule());
return objectMapper;
}
public static class DataSourceObject<T extends Serializable> implements Serializable {
private static final long serialVersionUID = -6026669040755678830L;
private final T concreteObject;
public DataSourceObject(final T concreteObject) {
this.concreteObject = concreteObject;
}
public T getConcreteObject() {
return concreteObject;
}
}
public static class HotelsListBean implements Serializable {
private final static long serialVersionUID = 1L;
protected String version;
protected MetaResponse metaResponse;
protected HotelsList hotelsList;
public String getVersion() {
return version;
}
public void setVersion(String value) {
this.version = value;
}
public MetaResponse getMetaResponse() {
return metaResponse;
}
public void setMetaResponse(MetaResponse value) {
this.metaResponse = value;
}
public HotelsList getHotelsList() {
return hotelsList;
}
public void setHotelsList(HotelsList hotelsList) {
this.hotelsList = hotelsList;
}
}
public static class MetaResponse implements Serializable {
private final static long serialVersionUID = 1L;
protected String returncode;
protected String date;
public String getReturncode() {
return returncode;
}
public void setReturncode(String value) {
this.returncode = value;
}
public String getDate() {
return date;
}
public void setDate(String value) {
this.date = value;
}
}
public static class HotelsList implements Serializable {
private final static long serialVersionUID = 1L;
protected List<Hotel> hotel;
public List<Hotel> getHotel() {
if (hotel == null) {
hotel = new ArrayList<Hotel>();
}
return this.hotel;
}
}
public static class Hotel implements Serializable {
private final static long serialVersionUID = 1L;
protected String name;
protected String code;
protected String nbstars;
protected String countryCode;
public String getName() {
return name;
}
public void setName(String value) {
this.name = value;
}
public String getCode() {
return code;
}
public void setCode(String value) {
this.code = value;
}
public String getNbstars() {
return nbstars;
}
public void setNbstars(String value) {
this.nbstars = value;
}
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String value) {
this.countryCode = value;
}
}
}
I would like to know what is possible to successfully deserialize.
(I tried to add #JsonCreator on the DataSourceObject constructor but I get the same exception)
You need to have a no-args constructor for DataSourceObject. You have two options.
Make concreteObject not final:
public static class DataSourceObject<T extends Serializable> implements Serializable {
private static final long serialVersionUID = -6026669040755678830L;
private T concreteObject;
private DataSourceObject() { }
public DataSourceObject(final T concreteObject) {
this.concreteObject = concreteObject;
}
public T getConcreteObject() {
return concreteObject;
}
}
Keep concreteObject final, but it must be assigned to null in the no-args constructor:
public static class DataSourceObject<T extends Serializable> implements Serializable {
private static final long serialVersionUID = -6026669040755678830L;
private final T concreteObject;
private DataSourceObject() {
concreteObject = null;
}
public DataSourceObject(final T concreteObject) {
this.concreteObject = concreteObject;
}
public T getConcreteObject() {
return concreteObject;
}
}
Either option will work.

ElementCollection inside Embeddable inside MappedSuperclass

I have problems with persisting the following construct with JPA.
#MappedSuperclass
public abstract class SuperFoo {
#Embedded
private Bar bar;
public SuperFoo() {}
public SuperFoo(...) {
...
this.bar = new Bar("barKey");
}
...
public void setBar(Bar bar) {
this.bar = bar;
}
public Bar getBar() {
return bar;
}
}
#Embeddable
public class Bar {
#ElementCollection
Map<String, Long> durations;
#ElementCollection
Map<String, Integer> integers;
public Bar() {}
public Bar(String barKey) {
his.durations = new HashMap<String, Long>();
this.integers = new HashMap<String, Integer>();
durations.put(key, new Long(15));
integers.put(key, new Integer(10));
}
... setters and getters.....
}
#Entity
public class Foo extends SuperFoo {
#Id
private String id;
public Foo() {}
public Foo(String id, *superArguments*) {
super(*superArguments*);
this.id = id;
}
public String getId() { return id; }
public void setId(String id) {
this.id = id;
}
}
#Entity
public class FooContainer {
#Id
String id;
#OneToMany(cascade = CascadeType.ALL)
List<Foo> fooList;
public FooContainer() {}
public FooContainer(String id) {
this.id = id;
this.fooList = new ArrayList<>();
}
public void addFoo(Foo foo) {
this.fooList.add(foo);
}
public void setFooList(List<Foo> fooList) {this.fooList = fooList;}
public List<Foo> getFooList() { return fooList; }
}
Method where persisting takes place:
public void method() {
FooContainer fooContainer = fooContainerRepository.getOne(...);
fooContainer.addFoo(new Foo(...));
fooContainerRepository.save(fooContainer);
}
JPA creates the following tables (looks fine):
foo
foo_durations
foo_integers
Now, when I save an instance of Foo, everything gets persisted (fields in Foo). BUT I get no entries in foo_durations and foo_integers. Furthermore, I get no exceptions.
I did a little research myself and found the following:
"An embeddable class that is contained within an element collection must not contain an element collection."
I think, here, that is not the case because my embeddable class "Bar" is not contained within an element collection. So does anybody know, what I might have done wrong?
Thanks in advance!
Update:
I forgot to mention that the Bar Object inside SuperFoo is not set via constructor but calculated inside the constructor.

Jackson: Object identity serializaton/deserialization based on implemented interface

interface Foo
public String key()
class Bar implements Foo
public int id;
public String name;
public Bar2 bar2; <--- bar2.key() should be used as json value
String key() { return name }
class Bar2 implements Foo
public int id;
public int name;
public Bar bar; <--- bar.key() should be used as json value
String key() { return name }
Whenever any object of type Foo is referenced in serialization, it's value should be object.key().
For deserialization, the value to should be used to lookup the actual object (Bar, Bar2, etc)
How can this be done with Jackson?
you need a getter method for the common property. Change Foo into abstract class and define the property and getter method there.
public abstract class Foo implements Serializable{
public String name;
public Foo bar;
public Foo() {
}
public String getBar(){
return bar.name;
}
public void setBar(Foo bar) {
this.bar = bar;
}
}
class Bar extends Foo{
public int id;
public Bar() {
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Bar bar = new Bar();
Bar2 bar2 = new Bar2();
bar.id = 1; bar.name = "bar1";bar.setBar(bar2);
bar2.id = 2; bar2.name = "bar2"; bar2.setBar(bar);
System.out.println(mapper.writeValueAsString(bar));
}
}
public class Bar2 extends Foo {
public int id;
public Bar2() {
}
}

Hibernate mapping non-entity class with List

I have 3 classes - Patient, AllergyList and Allergy. Both Patient and Allergy are #Entity and AllergyList simply has a variable with a list of Allergiest.
#Entity
public class Allergy {
private String id;
private String name;
public Allergy(String i, String n) {
id=i;
name=n;
}
#Id
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String n) {
this.name = n;
}
}
The Allergy class MUST have an id, and is referenced by other class in my program.
public class AllergyList {
private List<Allergy> allergies = new ArrayList<Allergy>();
public List<Allergy> getAllergies() {
return allergies;
}
public void setAllergies(List<Allergy> a) {
this.allergies = a;
}
}
I need to have this class, because I have inherited the program, and cannot change the existing codebase.
#Entity
public class Patient {
private String id;
private AllergyList allergyList = new AllergyList();
public Patient() {}
#Id
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public AllergyList getAllergyList() {
return allergyList;
}
public void setAllergyList(AllergyList allergyList) {
this.allergyList = allergyList;
}
}
And the main...
public static void main(String[] args) {
Patient patient = new Patient();
patient.setId("100001");
List<Allergy> allergies = new ArrayList<Allergy>();
allergies.add(new Allergy("1","Dust"));
allergies.add(new Allergy("3","Apple"));
allergies.add(new Allergy("4","Bee"));
patient.allergyList.setAllergies(allergies);
.
.
session.save(patient);
.
.
}
So the above three classes are what I'm given. I need to create allergy_list table that contains the following:
allergy_list
patient_id
allergy_id
I know I can create a List inside the Patient class, but the way the program is written I need to access the Allergy through the AllergyList class. How can I annotate to make this happen? Is it even possible?

What is equivalent code settings for #JSonIgnore annotation?

I'm new to Java and Jackson and a lot of other technologies which I try to use, so I'd appreciate a detailed answer.
Is there a way to prevent one or more fields from being serialized using Jackson into a JSON String_like format, but without using any kind of JSON annotations?
Something like: mapper.getSerializationConfig().something(ignore("displayname")) if you know what I mean.
My object is an instance of a class that extends another one, and implements one interface also so on, so the fields come from an hierarchy of classes.
I need the JSON representation for that object but containing only certain fields, so I can send that JSON in a mock request through a POST method.
I'm using Jackson 2.2.2.
If you can't change your classes you can create new abstract class/interface with methods with #JsonIgnore annotation. In this class/interface you can define methods which ObjectMapper should skip during serialization/deserialization process.
Please, see below example:
import java.io.IOException;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonProgram {
public static void main(String[] args) throws IOException {
Person person = new Person();
person.setId(1L);
person.setName("Max");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(Person.class, PersonMixIn.class);
System.out.println(objectMapper.writeValueAsString(person));
}
}
abstract class Entity {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
interface Namamble {
String getName();
}
class Person extends Entity implements Namamble {
private String name;
#Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
interface PersonMixIn {
#JsonIgnore
String getName();
}
EDIT - answer for the comments
You can create such mixin interface:
public static interface UserInformationMixIn {
#JsonIgnore
String getField3();
}
and configure ObjectMapper in this way:
objectMapper.addMixInAnnotations(UserInformation.class, UserInformationMixIn.class);
In version 2.5 method addMixInAnnotations was deprecated and addMixIn should be used:
objectMapper.addMixIn(UserInformation.class, UserInformationMixIn.class);
Full example source code:
import java.io.IOException;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonProgram {
public static void main(String[] args) throws IOException {
UserInformation userInformation = new UserInformation();
userInformation.setField3("field3");
userInformation.setField4("field4");
userInformation.setField5("field5");
User user = new User();
user.setField1(userInformation);
user.setField2("field2");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(UserInformation.class, UserInformationMixIn.class);
objectMapper.addMixIn(User.class, UserInformationMixIn.class);
System.out.println(objectMapper.writeValueAsString(user));
}
public static abstract class Someclass {
String field5;
public String getField5() {
return field5;
}
public void setField5(String field5) {
this.field5 = field5;
}
}
public static class UserInformation extends Someclass {
String field3;
String field4;
public String getField3() {
return field3;
}
public void setField3(String field3) {
this.field3 = field3;
}
public String getField4() {
return field4;
}
public void setField4(String field4) {
this.field4 = field4;
}
}
public static class User {
UserInformation field1;
String field2;
public UserInformation getField1() {
return field1;
}
public void setField1(UserInformation field1) {
this.field1 = field1;
}
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
}
public static interface UserInformationMixIn {
#JsonIgnore
String getField3();
#JsonIgnore
String getField2();
#JsonIgnore
String getField5();
}
}
Helpful link:
How can I tell jackson to ignore a property for which I don't have
control over the source code?

Categories

Resources