Jackson deserialize JSON with extra properties using #JsonRootName - java

I want to deserialize an object which is annotated with #JsonRootName. However the JSON in which the object is transported contains another extra property. As a result Jackson is complaining with:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (FIELD_NAME), expected END_OBJECT: Current token not END_OBJECT (to match wrapper object with root name 'account'), but FIELD_NAME at [Source: (ByteArrayInputStream); line: 1, column: 26].
Apparently deserialization of #JsonRootName annotated objects works ONLY if that object is the sole property in JSON file - since it's not expecting the "lastTransactionID" to be there.
Here's my Account class:
#JsonRootName("account")
public class Account {
private String id;
}
This is the JSON I need to deserialise:
{
"account": {
"id": "1234"
},
"lastTransactionID": "1"
}
Since I'm using spring I have this setup also spring.jackson.deserialization.unwrap_root_value=true.
Is there any way to solve this without:
having to write a custom deserializer?
OR
intercepting the response and stripping it of the extra property before deserialization takes place?

It looks like simplest way to solve this issue is create wrapper for Account class and deserialise json as usual with disabled DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES feature.
public static class Wrapper {
public Account account;
}
In this case other fields will be ignored.
It's not a nice solution, I know, but it solve a problem.

We can use ObjectMapper to Map json to java Objects.
public Account jsonToObject(Map<String, Object> map){
ObjectMapper objectMapper = new ObjectMapper();
Account account = objectMapper.convertvalue(map.get("account"),Account.class);
return account;
}

You can use JsonIgnoreProperties(ignoreUnknown=true) annotation on your Account class. Please refer below link for more details.
https://www.thetechnojournals.com/2019/10/entity-object-conversion-to-dto-object.html

Related

In jackson 2.8, how can I postprocess every deserialized value of class Foo?

I'm building a rest service using jackson with a single instance of ObjectMapper where I can set my configuration. Java-side values are pojos with fields of types like String and int. Very simple, straightforward situation, nothing special.
I want to perform some processing on every field of a given type after deserialization, possibly altering the value that should be put in the pojo field. I don't want to litter my pojos with annotations or anything, it should be self-contained within ObjectMapper. I also don't want to override the existing deserialization code - the data mapping itself should keep working as-is.
Concrete example: say I want to call toUpperCase() on every incoming String because I dislike lower case letters. How can I create this behavior? I was hoping to find something like the following, but it doesn't seem to exist:
objectMapper.getDeserializationConfig().registerValueProcessor(Foo.class, Foo::bar);
I'm familiar with jackson basics like registering a new type (de)serializer, I just don't know anything for this particular type of thing.
Concrete example: say I want to call toUpperCase() on every incoming String because I dislike lower case letters. How can I create this behavior?
Create your custom deserializer for String:
SimpleModule module = new SimpleModule("customDeserializers", Version.unknownVersion());
module.addDeserializer(String.class, new StdDeserializer<String>(String.class) {
#Override
public String deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
return jp.getValueAsString().toUpperCase();
}
});
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
The custom deserializer defined above will process only values mapped to String. If a given POJO has a member where its type is UUID, for instance, the value won't be converted to upper case. See the example below:
public class Person {
private UUID id;
private String name;
// Getters and setters
}
String json = "{\"id\": \"6d39b716-ee80-4468-b1e4-b4270d57be99\", \"name\": \"Joe Doe\"}";
Person person = mapper.readValue(json, Person.class);
System.out.println(person.getId()); // Prints 6d39b716-ee80-4468-b1e4-b4270d57be99
System.out.println(person.getName()); // Prints JOE DOE
I guess the best thing to do would be to write my own (de)serializer impl that wraps around an existing (de)serializer, using inheritance or composition, and replaces it in the objectmapper configuration. That way I can call the original conversion logic (through super or a field) and postprocess the result it returns.

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

Jackson crash when try to map a JSON without an element of the class

I'm having problems mapping an object using Jackson.
The issue happens when I map a JSON object that sometimes is missing an item from the class.
I'm trying to find out how to set up the configurations in order to not crash when the JSON does not have all the fields of the class.
I've already tried with:
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
#JsonIgnoreProperties(ignoreUnknown = true)
This is the error:
com.fasterxml.jackson.databind.JsonMappingException: Instantiation of [simple type, class com.non.real.package.Like] value failed: null (through reference chain: com.non.real.package.CardFeed["likes"]->com.non.real.package.CardLikes["likes"]->java.util.ArrayList[0])
"likes": {
"count": 0,
"likes": []
}
Trying different solutions I've found out that the Like object is extending the class Model of ActiveAndroid. Removing that "extension" and it works fine. I think the Model class does not work fine when it has a NULL or EMPTY.
For Jackson you could try changing the mapper's configuration directly:
mapper.setSerializationInclusion(Include.NON_NULL);
But may I suggest you look at another library to map JSON to POJO? Both GSON and Genson (my personal favorite) do the same, but much faster, and much easier. Take a look at the benchmarks here, where they compare the (de)serialization of Jackson, GSON and Genson.
With Genson, it's very easy to skip null values:
private static final Genson gensonSkipNulls = new Genson.Builder().setSkipNull(true).create();
/**
* Deserializes JSON into a Java object.
*
* #param json The JSON to deserialize.
* #param superclass The model to deserialize the JSON into.
* #return An object that is an instanceof superclass.
*/
public Object deserialize(final String json, final Class superclass) {
return genson.deserialize(json, superclass);
}

Jackson: how to read a String as List?

Our current class looks like
public class Attributes {
private String mapping;
.....
and
{
"mapping": "displayName",
.....
This has worked well and shipped to customers.
The way we convert JSON to Attribute class is
JSON.readValue(jsonFile, Attribute.class);
Recently the requirement says, that mapping would be List<String> instead of String
At first, the quick change that I thought of was to change mapping to List<String>, but this would break existing clients.
I tried that by writing test that does
assertEquals("displayName", configuration.getMapping().get(0));
and it failed as
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token
Question
How can I tell Jackson to read a String as `List? It would be List of 1 item but would be backward compatible.
Thanks
The answer is Can not deserialize instance of java.util.ArrayList out of VALUE_STRING
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);

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.

Categories

Resources