Suppose I have a json object that looks like:
{
id: 1,
name: "john doe"
spouse: 2
}
and the class I want it to deserialize it to:
class Person{
private Long id;
private String name;
private Person spouse;
//getters/setters
}
Is there any way to tell jackson to expand the spouse: 2 property into a new Person POJO with id=2 when deserializing the JSON?
I have run into this issue as a result of deserializing JSON into persistent entities and would like to be able to easily persist the relationships between these entities.
Aside from a full deserializer, there is a simpler way: define a POJO with a single int-arg constructor like so:
class Person {
int id;
public Person(int id) {
this.id = id;
}
}
This actually works, as Jackson will try to find limited number of special constructors (single-arg public constructors that that take String, int, long, double or boolean).
You can optionally also denote these with #JsonCreator -- and if constructor is not public, you must do it, to make it discoverable. But for public ctors this is not needed.
It is impossible of course for Jackson to infer a fully populated Person object representing the spouse from the number 2. You would likely need to register a custom deserializer that checks if the input is an integer, and if so, looks up the spouse from wherever it is stored. I have not done this kind of thing for classes that contain references to themselves (e.g. your Person contains a Person) so I can only give you rough guidance.
I believe this may only work with Jackson version 1.9 or later. Basically, you can register a module with the object mapper that tells Jackson to use a custom deserializer.
SimpleModule module = new SimpleModule("PeopleModule", new Version(1, 1, 0, null);
module.addDeserializer(Person.class, new JacksonPersonDeserializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
Alternately on the Person class itself, you can do something like:
class Person {
#JsonDeserialize(using=JacksonPersonDeserializer.class)
Person spouse;
}
This works before 1.9 but pollutes your object. Either way, you will need to write a custom deserializer.
Related
I am trying to build following json structure via my objects
{
Id:"123",
Name:"me",
Attributes:[{x:"test1",value:{value1:"1"},{x:"test2",value:[{abc:"somevalue"}]}}
]
Attributes size will be fixed and value of x would be same in each request.
What would be ideal structure of classes in jackson mapping
Seems like a structure like this would do.
Of course you'll need some additional annotations to ensure the fields have the proper names, and you'll also want to ass getters and setters.
class Outer{
String id;
String name;
List<Attribute> attributes;
}
class Attribute{
String x;
Map<String, String> values;
}
I would like to serialize to JSON a POJO containing another POJO with empty values.
For example, given:
class School {
String name;
Room room;
}
class Room {
String name;
}
Room room = new Room();
School school = new School("name");
school.room = room;
After the serialisation it will look like that
{ "name": "name", "room": {}}
Is it possible to exclude the empty object {} if all the fields of the class are also empty? Ideally globally for every object without writing custom code.
A little bit late , add JsonInclude custom
class School {
String name;
#JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = Room .class)
Room room;
}
#EqualsAndHashCode
class Room {
String name;
}
This will avoid to include room if is equals than new Room().
Don't forget implement equals method in Room class and include an empty constructor
Besides you can use #JsonInclude( Include.NON_EMPTY ) or #JsonInclude( Include.NON_DEFAULT ) at class level to skip possible nulls or empty values
TLDR: the behavior you want won't work because the Object has been instantiated, needs to be null.
Include.NON_EMPTY
Include.NON_NULL
The reason these options don't work with what you are trying to do is because you have instantiated an Object of Room type so Room is not empty or null hence your output: { "name": "name", "room": {}}
If you effectively want to avoid having Room represented in your JSON, then the Object needs to be null. By setting school.room = null you would get your desired output, though in the real world this could become messy as you'd have to evaluate if fields in the Object were actually null before setting Room in School to be null.
A custom serializer would handle this better, see: Do not include empty object to Jackson
Add #JsonInclude(Include.NON_EMPTY) to remove empty objects:
#JsonInclude(Include.NON_EMPTY)
class School {
Value that indicates that only properties with non-null values are to be included.
Let's say we have an Object A defined like this:
public class ObjectA {
private Attribute a1;
private Attribute a2;
private Attribute a3;
}
For some reason, I need to create a second object B with only the first two attriutes of the Object A :
public class ObjectB {
private Attribute a1;
private Attribute a2;
}
So my question is: what is the best approach to copy an Object A to an Object B ?
I've been copying the attributes by getters and setters one by one but something tells me there must be a better way to do this ! Especially when the object will have a lot of attributes, I have to write lines and lines of code just to copy all of them to the second Object B ...
Thanks a lot :)
EDIT: I've been being alerted by a "possible duplicate of another question" : How do I copy an object in Java?
My question is slightly different in a way that I'm dealing with 2 different objects who just share the same attributes but not totally !
Try libraries like Dozer or BeanUtils
To expand on my comment:
Using Dozer it can be as easy as:
Mapper mapper = new DozerBeanMapper();
ObjectA source = new ObjectA();
ObjectB target = mapper.map(source , ObjectB.class);
or if your target class doesn't have a no-arg constructor:
ObjectA source = new ObjectA();
ObjectB target = new ObjectB(/*args*/);
mapper.map(source, target );
From the Documentation (emphasis by me):
After performing the Dozer mapping, the result will be a new instance of the destination object that contains values for all fields that have the same field name as the source object. If any of the mapped attributes are of different data types, the Dozer mapping engine will automatically perform data type conversion.
What you need is Object mappers. Try Orika or Dozer.
The objects need not be of the same type. While mapping if it finds the attributes of the same name and type, it automatically maps it.
MapperFacade mapper = mapperFactory.getMapperFacade();
UserDTO userDTO = new UserDTO();
userDTO.setName("xyz");
..
User user = mapper.map(userDTO, User.class);
You can also customize if you have to map different attribute names.
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(UserDTO.class, User.class)
.field("name", "username")
.byDefault().register();
mapper = mapperFactory.getMapperFacade();
...
User user = mapper.map(userDTO, User.class);
Suppose I have JSON that looks something like this:
{ "key1":1, "key2":2, "key3":3 }
Where the number of "key-n" fields is unknown, but are consecutively numbered starting at 1. I wish to deserialize it into an object as follows:
public class MyPojo {
private List<Integer> keys;
}
That is, keys.get(0) corresponds to the key1 field, and so on. The JSON may have other non-"key-n" fields as well.
I had been under the impression that something like
public class MyPojo {
#JsonUnwrapped #JsonDeserialize(using = KeyDeserializer.class) private List<Integer> keys;
}
where KeyDeserializer is a JsonDeserializer would just extract all of the "key-n" fields, would work; however, I had discovered that the deserializer isn't being invoked because the JSON lacks a field named key.
Since the JSON is third-party, I can't really try to modify the JSON, so I am wondering if there are any alternate approaches to this problem.
There is a question on my mind for a while. Let's say we have the following classes:
public Class Person{
String name;
String address;
String description;
}
public Class PersonFacade{
String name;
String address;
String desc;
}
as you can see the only difference between these two classes are the name of one variable. My question is what is the best way to write a helper class to map the values of one object to another object. Let's assume we have the following:
Person person = new Person();
person.name="name1";
person.address="address1";
person.description="description1";
I want to write a class that is supposed to do the following (let's call it Transformer class)
PersonFacade personFacade = new PersonFacade();
TransformClass.transformFrom(person, personFacade);
What I want this TransformClass.transformFrom() method to do is the follwoing:
based on the similarity of the variable names, assign the value of the variable from "FromClass" to "ToClass"
so in our case, I want it to assign personFacade.name = "name1", personFacade.address="adderss1" and personFacade.desc = "description1" (this last one seems harder to accomplish, but let's try)
Any ideas?
You can use Dozer:
Dozer is a Java Bean to Java Bean mapper that recursively copies data
from one object to another. Typically, these Java Beans will be of
different complex types.
Dozer supports simple property mapping, complex type mapping,
bi-directional mapping, implicit-explicit mapping, as well as
recursive mapping. This includes mapping collection attributes that
also need mapping at the element level.
Look at this: http://dozer.sourceforge.net/
It's a great JavaBean Mapper.
Here the "Getting Started":
http://dozer.sourceforge.net/documentation/gettingstarted.html
Perhaps you can write your own Annotation class in order to create the relationship between the classes. So, for example
public Class Person{
#MyAnnotation(id='name')
String name;
#MyAnnotation(id='addr')
String address;
#MyAnnotation(id='desc')
String description;
}
public Class PersonFacade{
#MyAnnotation(id='name')
String name;
#MyAnnotation(id='addr')
String address;
#MyAnnotation(id='desc')
String desc;
}
Then in your TransformClass, you simply need to iterate through the annotations, find a match and set the corresponding field value with the help of Reflection.