how to use #JsonProperty in this case - jackson API with lombok - java

I know how to use #JsonProperty in jackson API but the below case is different.
I've below json snippet.
{"results":[{"uniqueCount":395}
So, to parse with jackson API the above json, I've written below java pojo class.
package com.jl.models;
import lombok.Data;
#Data
public class Results
{
private int uniqueCount;
}
Later, I got to parse below similar json snippet.
{"results":[{"count":60}
Now, the problem here is I'm unable to parse this json with Results class as it expects a string uniqueCount.
I can easily create another java pojo class having count member variable but I've to create all the parent java classes having instance of Results class.
So, is there any way I can customize Results class having lombok behaviour to parse both the json without impacting each others?
Thanks for your help in advance.

You can use Jackson's #JsonAnySetter annotation to direct all unknown keys to one method and there you can do the assignment yourself:
#Data
public class Results
{
private int uniqueCount;
// all unknown properties will go here
#JsonAnySetter
public void setUnknownProperty(String key, Object value) {
if (key.equals("count")) {
uniqueCount = (Integer)value;
}
}
}

Related

Why can’t Jackson deserialize single field class with RequiredArgsConstructor?

If I have a class using Lombok:
#RequiredArgsConstructor
#Getter
class Example {
private final String id;
}
And try to deserialize it from
{
“id”: “test”
}
Jackson throws an exception that although at least one creator was provided, it could not deserialize.
If I then add another final String field to that class, and add that field to the JSON, it is deserialized with no complaints.
Does anyone know what’s going on here? Why are you unable to deserialize if you only have one field?
When only way to intialize object properties is through contructor, Jackson needs to be told that deserialization should happen using constructor via #JsonCreator annotation.
Also, all the property names should be provided via #JsonProperty annotation because Jackson needs to know the sequence of attributes passed in contructor to correctly map json values to Java object attributes.
So, if you are not using lombok contructor, then constructor will look like
#JsonCreator
public Example (#JsonProperty("id") String id) {
this.id = id;
}
If you don't want to manually write the contructor, go ahead with #tashkhisi's answer.
Also, I highly doubt following could happen. Could you update the question with code showing this?
If I then add another final String field to that class, and add that field to the JSON, it is deserialized with no complaints.

Object serialization to json, certain fields only

I have a large nested object. I want to serialise this object in the JSON string, however I need only certain fields to be included. Problem here is that fields could change very frequently and I want to build it in a way that could help me easy include or exclude fields for serialisation.
I know that I can write a lot of code to extract certain fields and build JSON "manually". But I wonder if there are any other elegant way to achieve similar outcome but specifying a list of required fields?
For example having following object structure I want include only id and name in the response:
class Building {
private List<Flat> flats;
}
class Flat {
private Integer id;
private Person owner;
}
class Person {
private String name;
private String surname;
}
Json:
{
"flats" : [
{
"flat":
{
"id" : "1",
"person" : {
"name" : "John"
}
}
}
]
}
You can use gson for serializing/deserializing JSON.
Then you can include the #Expose annotation to use only the fields you require.
Be sure to also configure your Gson object to only serialize "exposed" fields.
Gson gson = GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Alternative:
You can actually do it the inverse way, marking fields which will not be exposed. You can do this with the transient keyword.
So whatever you want to ignore just add transient to it. Here's how it works on gson.
PS: This works on most Java JSON serializers too.
Using com.fasterxml.jackson.annotation.JsonIgnore is another way to achieve this.
import com.fasterxml.jackson.annotation.JsonIgnore;
class Person {
private String name;
#JsonIgnore
private String surname;
}
It will ignore the surname when the parser converts the bean to json.
Similar annotation will be available in other json processing libraries.
If using Gson, study how to use ExclusionStrategy & JsonSerializer.
Using those is a more flexible way to control serialization since it allows to decide per serialization what to serialize.
Using annotations requires later to add / remove those annotations from fields if there is a need to change what to serialize.
In the case of your example the latter might be more appropriate.
This question might be good startpoint
serialize-java-object-with-gson

Deserializing polymorphic JSON with Gson throwing exception

I'm working on an app that is using Gson as JSON deserializer and needs to deserialize polymorphic JSON from REST API. Before explaining mi issue note that I've already been looking around polymorphic deserialization with Gson and have implemented it on several cases successfully. So this is a specific issue I'm having. Also I've read this great post and this Stack Overflow discussion before asking this. I'm using RuntimeTypeAdapterFactory to deserialize polymorphic objects by the way.
The problem I'm having is that apparently GSON's RuntimeTypeAdapterFactory does't allow to declare the field which specifies the type of the object inside the hierarchy structure. I'll explain further with some code. I have the following pojos structure (pojos had been reduced for the sake of simplicity):
public abstract class BaseUser {
#Expose
protected EnumMobileUserType userType;
}
public class User extends BaseUser {
#Expose
private String name;
#Expose
private String email;
}
public class RegularUser extends User {
#Expose
private String address;
}
public class SpecialUser extends User {
#Expose
private String promoCode;
}
Now this is the code where I defined the RuntimeTypeAdapterFactory for User hierarchy.
public static RuntimeTypeAdapterFactory<BaseUser> getUserTypeAdapter() {
return RuntimeTypeAdapterFactory
.of(BaseUser.class, "userType")
.registerSubtype(User.class, EnumMobileUserType.USER.toString())
.registerSubtype(RegularUser.class, EnumMobileUserType.REGULAR.toString())
.registerSubtype(SpecialUser.class, EnumMobileUserType.SPECIAL.toString());
}
public static Gson getGsonWithTypeAdapters() {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapterFactory(getUserTypeAdapter());
return builder.create();
}
Now when I try to deserialize a JSON text:
{
"user":{
"userType":"USER",
"email":"albert#gmail.com",
"name":"Albert"
}
}
I get this exception:
com.google.gson.JsonParseException: cannot serialize com.mobile.model.entities.v2.common.User because it already defines a field named userType
But if I change the name of the property "userType" in my BaseUser class to "type" for example and I deserialize the same JSON everything works properly. I don't get why Gson RuntimeTypeAdapterFactory has this restriction. In fact in this blog post apparently this is not an issue.
Can anyone explain what's going on here, why the name of the property that defines the type cannot be defined inside the pojos hierarchy?
EDIT the issue is not when deserializing but when serializing using the code described above. Find further explanation in the answer.
Well, after some time digging I found out that the problem is not actually deserializing, the problem comes when serializing and having a RuntimeTypeFactory registered as described in the question. If you register a runtimeTypeAdapterFactory and use the same field name to define the class type in the factory and in your pojo, json resulting from serializing pojo to json using GSON with the RuntimeTypeAdapterFactory for a SpecialUser for example will be:
{
"user":{
"userType":"SPECIAL",
"email":"albert#gmail.com",
"name":"Albert"
"userType":"SPECIAL"
}
}
This will result in the exception described:
com.google.gson.JsonParseException: cannot serialize com.mobile.model.entities.v2.common.User because it already defines a field named userType
because de field userType is repeated in the json due to GSON serializer, which will automatically add a field as declared in the RuntimeTypeAdapterFactory registered for the class BaseUser.
I think that using your own userType without #Expose annotation will do the trick
Regads
You can always use a default Gson instance to serialize (new Gson()) and then use the RuntimeTypeAdapterFactory instance to deserialize.
It is recommended not to use #Expose if you want everything to be transformed. It only blows up your model classes with redundant annotations.

Jackson parse json with unwraping root, but without ability to set #JsonRootName

The rest service responds with
<transaction><trxNumber>1243654</trxNumber><type>INVOICE</type></transaction>
or in JSON:
{"transaction":{"trxNumber":1243654,"type":"INVOICE"}}
There is no problems when I use:
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true)
And as resulting class
#JsonRootName("transaction")
public class Transaction {
private String trxNumber;
private String type;
//getters and setters
}
But actually I should use the Transaction class from 3rd party jar, which is exact like above, but has no #JsonRootName("transaction") annotation.
So I end up with
Could not read JSON: Root name 'transaction' does not match expected ('Transaction') for type...
Is there any ways to force Jackson parse to Transaction class without adding any stuff to the Transaction class itself (as I get this file as part of a binary jar)?
I've tried custom PropertyNamingStrategy, but it seems has to do only with field and getter/setter names, but not class names.
Java7, Jackson 2.0.5.
Any suggestions? thanks.
You can do it with mixin feature. You can create simple interface/abstract class like this:
#JsonRootName("transaction")
interface TransactionMixIn {
}
Now, you have to configure ObjectMapper object:
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
mapper.addMixInAnnotations(Transaction.class, TransactionMixIn.class);
And finally you can use it to deserialize JSON:
mapper.readValue(json, Transaction.class);
Second option - you can write custom deserializer for Transaction class.

What's the Jackson deserialization equivalent of #JsonUnwrapped?

Say I have the following class:
public class Parent {
public int age;
#JsonUnwrapped
public Name name;
}
Producing JSON:
{
"age" : 18,
"first" : "Joey",
"last" : "Sixpack"
}
How do I deserialize this back into the Parent class? I could use #JsonCreator
#JsonCreator
public Parent(Map<String,String> jsonMap) {
age = jsonMap.get("age");
name = new Name(jsonMap.get("first"), jsonMap.get("last"));
}
But this also effectively adds #JsonIgnoreProperties(ignoreUnknown=true) to the Parent class, as all properties map to here. So if you wanted unknown JSON fields to throw an exception, you'd have to do that yourself. In addition, if the map values could be something other than Strings, you'd have to do some manual type checking and conversion. Is there a way for Jackson to handle this case automatically?
Edit:
I might be crazy, but this actually appears to work despite never being explicitly mentioned in the documentation: http://fasterxml.github.io/jackson-annotations/javadoc/2.2.0/com/fasterxml/jackson/annotation/JsonUnwrapped.html
I was pretty sure it didn't work for me previously. Still, the proposed #JsonCreator approach might be preferred when custom logic is required to deserialize unwrapped polymorphic types.
You can use #JsonCreator with #JsonProperty for each field:
#JsonCreator
public Parent(#JsonProperty("age") Integer age, #JsonProperty("firstName") String firstName,
#JsonProperty("lastName") String lastName) {
this.age = age;
this.name = new Name(firstName, lastName);
}
Jackson does type checking and unknown field checking for you in this case.
It does work for deserialization as well, although it's not mentioned in the docs explicitly, like you said. See the unit test for deserialization of #JsonUnwrapped here for confirmation - https://github.com/FasterXML/jackson-databind/blob/d2c083a6220f2875c97c29f4823d9818972511dc/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrapped.java#L138
#JsonUnwrapped works for both serialization and deserialization, you shouldn't need to take any additional steps.
Despite not being mentioned in the Javadocs prior to Jackson 2.13 (per
jackson-annotations#184), the #JsonUnwrapped annotation does apply to deserialization as well as serialization, so no additional work is needed to support deserialization of a field using the annotation.
The Jackson 2.13 Javadocs for #JsonUnwrapped clarify that the annotation applies to deserialization as well as serialization:
Annotation used to indicate that a property should be serialized "unwrapped" -- that is, if it would be serialized as JSON Object, its properties are instead included as properties of its containing Object -- and deserialized reproducing "missing" structure.
[...]
When values are deserialized "wrapping" is applied so that serialized output can be read back in.
For those who googled here like me, trying to resolve issue when deserializing unwrapepd Map, there is a solution with #JsonAnySetter:
public class CountryList
{
Map<String, Country> countries = new HashMap<>();
#JsonAnySetter
public void setCountry(String key, Country value)
{
countries.put(key, value);
}
}

Categories

Resources