Jackson ignore empty objects "{}" without custom serializer - java

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.

Related

Will Gson set a field to null when JSON doesn't contain it?

I actually have multiple questions regarding Gson.
The first one being if Gson would set the value of a field to null when the provided JSON does not contain any field matching it.
For example, when the provided JSON features the field name but the class I deserialize it to contains name and avatar, would avatar be null?
The next question is in relation to the above one. When I would set a field with an already predefined value, would Gson override it, even if it isn't provided in the JSON (overrides it to null) or would it simply ignore the field and move on?
And finally would I want to know if Gson would still set a value to name when I would use #SerializedName("username") but the JSON contains name.
I want to update my API, including some bad namings of JSON fields, but I want to make the transition of it for the people using it a smooth as possible, so I want to still (temporary) provide the old field name, while also providing support for the new one. Is that possible using the #SerializedName annotation?
I'm still a beginner with Gson and the Gson User Guide wasn't that helpful for me to answer those two specific questions (Or I overlooked it which would also be possible).
I tried implementing this. Here is my code. I hope the output at the end answers your question.
JSON used:
{
"name": "Robert",
"weather": "19 deg"
}
Main class:
public class GSONExample2 {
private static final String jsonStr = "JSON Mentioned above";
public static void main(String[] args) {
GsonDataExample root = new Gson().fromJson(jsonStr, GsonDataExample.class);
System.out.println(root);
}
}
POJO:
class GsonDataExample {
#SerializedName("username")
private String name;
private String avatar;
#SerializedName(value="weather", alternate = "temperature")
private String weather;
private String nameWithDefault = "Default name";
// getters, setters and toString() implemented
}
Output:
GsonDataExample(name=null, avatar=null, weather=19 deg, nameWithDefault=Default name)
To map multiple keys to same attributes, you can use #SerializedName(value="weather", alternate = "temperature") as shown above.

How to use PolymorphicJsonAdapterFactory with an interface in Moshi?

I have an interface with two concrete types as part of my model that I'd like to serialize/deserialize with Moshi. My issue is that I don't fully understand if PolymorphicJsonAdapterFactory is actually meant for my use case. I've looked at the samples and a few blog posts and (if I'm understanding them correctly) all of them seem to point to the fact that your interface is supposed to have a field in the interface that allows you to determine the type. I'm working in an existing codebase and so I can't easily add a field that would allow me to figure out what type it is by some string literal.
This is where I'm at with my Moshi code and I'm seeking validation on whether or not I'm using PolymorphicJsonAdapterFactory correctly. Note: I'm using java for the moshi portion of the code and for my model. My interface and it's concrete types are in kotlin
String json = ...;
Moshi moshi = new Moshi.Builder()
.add(PolymorphicJsonAdapterFactory.of(PersonInterface.java, "")
.withSubtype(BusinessPerson.java, "occupation")
.withSubtype(PolicePerson.java, "rank")
)
.build();
JsonAdapter<MyModel> jsonAdapter = moshi.adapter(MyModel.class);
MyModel myModel = jsonAdapter.fromJson(json);
Note: I'm using java for the moshi portion of the code and for my model. My interface and it's concrete types are in kotlin
MyModel is defined as the following
class MyModel {
String month;
PersonInterface person;
}
My interface and concrete classes in kotlin:
Interface PersonInterface {
val personsName: String?
}
data class BusinessPerson(
override val personsName: String,
val occupation: String?
) : PersonInterface
data class PolicePerson(
override val personsName: String,
val rank: String?
) : PersonInterface
The goal is to hopefully have Moshi be able to create a
class MyModel {
String month;
BusinessPerson person;
}
or a
class MyModel {
String month;
PolicePerson person;
}
depending on whether or not the person field contains an occupation (which means it's of type BusinessPerson) or if it contains the field rank (which means it's of type PolicePerson).
PolymorphicJsonAdapterFactory.of(PersonInterface.java, "")
.withSubtype(BusinessPerson.java, "occupation")
.withSubtype(PolicePerson.java, "rank")
means that JSON for every person is supposed to contain a key "" and "occupation" or "rank" is the value for this key, not a key name itself as you want. So e.g. it would encode a
BusinessPerson("John", "CEO")
as
{"": "occupation", "personsName": "John", "occupation": "CEO"}
I think you'll have to write your own adapter factory for this use-case if you want to avoid a discriminator field.

Jackson json two way object reference

I have a pair of objects like
public class Obj1 {
public int id;
public String name;
public Obj2 obj2;
}
public class Obj2 {
public int id;
public String name;
public List<Obj1> obj1list;
}
I want to be able to convert this to Json via Jackson. I found the JsonManagedReference and JsonBackReference and annotated them but when you do that, the serialization only works in one way. It will only show when the class with the JsonManagedReference side is serialized.
If I serialize an "Obj1" I want to see the "Obj2" that is attached to it. And if I serialize the "Obj2" I want to see the list of "Obj1"s that is attached to it.
I also tried using JsonIdentityInfo annotation like so
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
and this seems to work except that it adds the "id" value of the parent object into the subobject (or every subobject in the list case) which is a bit odd. Though I guess I can live with it.
Is there a way to get this to behave as I expect?
You are looking at #JsonIgnoreProperties, it will give what is needed and avoid json recursion.
public class Obj1 {
public int id;
public String name;
#JsonIgnoreProperties("obj1list")
public Obj2 obj2;
}
public class Obj2 {
public int id;
public String name;
#JsonIgnoreProperties("obj2")
public List<Obj1> obj1list;
}
UPDATE
I always perfers #JsonIgnoreProperties over JsonBackReference and JsonManagedReference. As it not only does the trick, but also not escapes any data serialization (which is the case required here).
I also have a sample project on github deals with this situation. Look for entity class codes School.java & District.java. Its an mvn spring-boot executable code, if you wanna run a test.
From Javadoc, starting with Jackson 2.0, #JsonIgnoreProperties annotation can be applied both to classes and to properties. If used for both, actual set will be union of all ignorals: that is, you can only add properties to ignore, not remove or override. So you can not remove properties to ignore using per-property annotation.
HOW IT WORKS
When you define #JsonIgnoreProperties at propety level, while serialization/deserization it will work on the refered property object.
So here, when Obj1 is being parsed, you asked parser to ignore obj1list property of obj2. And similary, while parsing Obj2 it will ignore contained obj2 references in the Obj collection. So your parsing will look like below:
Obj1 : {
id : int,
name : string,
Obj2 : {
id : int,
name : string
obj1list : //ignored avoids recursion
}
}
Obj2 : {
id : int,
name : string,
obj1list : [{
id : int,
name : string,
obj2 : //ignored avoids recursion
},
{
id : int,
name : string
obj2 : //ignored avoids recursion
}
]
}

Jackson include null set by user?

I am serializing a POJO using jackosn, and I want that all the values for which the user sets some value irrespective whether it's null or not must be included in the serialization.
So currently:
POJO:
public class YourItem {
public String key;
public String item;
}
Currently when user does:
YourItem item = new YourItem();
item.setKey("abc");
The serialization gives:
{
"key" : "abc"
}
as I configured ObjectMapper as objectMapper.setInclude(Include.NON_NULL)
However now if the user specifically calls the setter and sets the value as null, then I want that item in my serialized string.
So if user does
YourItem item = new YourItem();
item.setKey("abc");
item.setItem(null);
I want in serialzation both key and item values are present like:
{
"key" : "abc",
"item" : null
}
How do I differentiate between the user set null and the default null.
Is there a configuration in ObjectMapper ??
Some people consider using null to be bad practice (The book Clean Code, for instance)
Disregarding that, you cannot differentiate between the default initialization null and a user-set null by language-design
You need some sort of state that tracks if a field has been accessed by a setter. If it hasn't been accessed by a setter and is null, ignore it.
One way to do this is Jackson Filters, which allows you to define various conditions for serializing a field during runtime (your condition being that your setter-access-state indicates that the field was set by a user)
http://www.baeldung.com/jackson-serialize-field-custom-criteria

Jackson Nested Object Deserialization into property

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.

Categories

Resources