Jackson: deserialization to a collection - java

I have this specific problem with JSON deserialization. Let's have this JSON structure:
{
"header":{
"objects":[
{"field":"value1"},
{"field":"value2"}
]
}
}
The JSON structure can't be altered as it comes from a 3rd party system.
Now let's have this simple POJO:
#JsonDeserialize(using=PojoDeserializer.class)
public class Pojo {
private string field;
//...getter, setter
}
The mentioned PojoDeserializer takes {"field": "value"} json string and deserializes it to the Pojo instance. So I can simply do the deserialization like this
Pojo instance = new
ObjectMapper().readValue("{\"field\":
\"value\"}", Pojo.class);
And here's my problem. Let's have another deserializer PojosCollectionDeserializer which takes the mentioned structure and deserializes it to a Collection of Pojo instances. I'd like to use it in a similar fashion as in the previous example:
Collection<Pojo> pojos = new ObjectMapper().readValue("{...}", Collection.class);
But this doesn't work as there is not defined that Collection should be created using the PojosCollectionDeserializer. Is there any way to achieve it?

I am not sure why are trying to explicitly specify deserializers, as it would all work just fine with something like:
public class Message {
public Header header; // or, if you prefer, getter and setter
}
public class Header {
public List<Pojo> objects;
}
public class Pojo {
public String field;
}
Message msg = objectMapper.readValue(json, Message.class);
without any additional configuration or annotations. There is no need to construct custom serializers or deserializers for simple cases like this.

Related

Use DTO to map JSON to final object, or just parse JSON?

I have some json object that looks like this:
{
"make":"Volvo",
"model":"240",
"metadata":{
"color":"white",
"year":"1986",
"previousOwner":"Joe",
"condition":"good"
}
}
And I want to turn this JSON into List<Car>, which is comprised of the following objects:
public class Car {
private String make;
private String model;
private CarMetadata carMetadata;
}
public class CarMetadata {
private Body body;
private History history;
}
public class Body {
private String color;
private String condition;
}
public class History {
private String previousOwner;
private String year;
}
So essentially the point is that the object I want to turn it into (Car) is very nested, whereas my JSON is not very nested. In reality the "Car" object is actually much more nested than this example I'm showing.
I was thinking of two options:
Create a CarDTO object to represent my input JSON, do objectMapper.readValue(json, CarDTO.class), then map CarDTO to Car to create my List<Car>.
Just parse the JSON and create the final List<Car> object in the first place.
I don't want to create an unnecessary DTO, but I also don't want to mess with parsing this JSON.
Is there a best practice in this scenario, and would this even be a valid use of a DTO?
Use a DTO.
Although you can deserialize from json directly to your domain class, their structure differs so you would have to create a custom deserializer... DO NOT TRY THIS AT HOME. I've been there and it's completely not worth the hassle.
Use the DTO to parse the json into a POJO, then map the DTO to the domain object.
This will decouple the transport from your domain object, allowing both to change freely with only the mapping code being affected. It's also way easier to write, understand, test and debug.

Gson custom deserializer for specific fields to String

I have a variety of JSON files (with slightly different schemas) flowing on Kinesis. Their Schema is really complex. They are user raw hits and their schema is super complex. I would like to create a Single POJO to represent all underneath messages (Something Spark does internally by creating a Single schema). I was trying to use GSON library but the only way to accomplish this is, by writing a custom deserializer and in that case, I will end up writing deserialization logic for all the fields in those JSONs.
Is there any way where we can only overwrite the deserialization of a few fields only and the rest of the fields can still be deserialized by GSON as a default way?
JSON-1
{
"first_col":"abc",
"second_col":false
}
JSON-2
{
"first_col":"abc",
"second_col":"false-String"
}
JSON-3
{
"first_col":"abc",
"second_col":{
"col1":"xyz",
"col2":123
}
}
Common POJO
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Example {
private String firstCol;
private String secondCol;
public String getFirstCol() {
return firstCol;
}
public void setFirstCol(String firstCol) {
this.firstCol = firstCol;
}
public Boolean getSecondCol() {
return secondCol;
}
public void setSecondCol(String secondCol) {
this.secondCol = secondCol;
}
}
So basically second_col could be boolean, string, or complex object. first_col is always a string. So I don't want to write deserialize logic for first_col. I want to write deserialize logic for only second_col and deserialize it to string only and the downstream consumer will take care of converting it to the right type before consuming this field.

Jackson not properly mapping properties to POJO

Been trying to map a certain JSON string that I'm receiving from an REST API call, but so far I only got the following message.
Unrecognized field "my_first_field" (class MyClass), not marked as ignorable
I've been parsing the contents with IOUtils class, the following way (maybe the encoding is the root cause).
String json = IOUtils.toString(responseEntity.getContent(), UTF_8);
Once this has been done, I try to map the payload String to my POJO class using ObjectMapper.
new ObjectMapper().readValue(json, new TypeReference<MyClass>(){ })
However, while performing that step the exception mentioned in the beginning is prompt. The POJO class is the following.
public static class MyClass {
#JsonProperty("my_first_field")
private List<Map<String, String>> myFirstField;
#JsonProperty("my_second_field")
private String mySecondField;
public MyClass() { }
public MyClass(List<Map<String, String>> myFirstField, String mySecondField) {
this.myFirstField = myFirstField;
this.mySecondField = mySecondField;
}
(...)
}
Ignoring those unknown fields result in all POJO fields being null. What could be the problem in here?
EDIT: Sample JSON
{"my_second_field":"samplevalue", "my_first_field":[{"inner":"value"}]}
Make sure you import #JsonProperty annotation from fasterxml package, not from anywhere else.

Deserialize a JSON wrapped in an object with an unknown property name using Jackson

I'm using Jackson to deserialize JSON from a ReST API to Java objects using Jackson.
The issue I've run into is that one particular ReST response comes back wrapped in an object referenced by a numeric identifier, like this:
{
"1443": [
/* these are the objects I actually care about */
{
"name": "V1",
"count": 1999,
"distinctCount": 1999
/* other properties */
},
{
"name": "V2",
"count": 1999,
"distinctCount": 42
/* other properties */
},
...
]
}
My (perhaps naive) approach to deserializing JSON up until this point has been to create mirror-image POJOs and letting Jackson map all of the fields simply and automatically, which it does nicely.
The problem is that the ReST response JSON has a dynamic, numeric reference to the array of POJOs that I actually need. I can't create a mirror-image wrapper POJO because the property name itself is both dynamic and an illegal Java property name.
I'd appreciate any and all suggestions for routes I can investigate.
The easiest solution without custom deserializers is to use #JsonAnySetter. Jackson will call the method with this annotation for every unmapped property.
For example:
public class Wrapper {
public List<Stuff> stuff;
// this will get called for every key in the root object
#JsonAnySetter
public void set(String code, List<Stuff> stuff) {
// code is "1443", stuff is the list with stuff
this.stuff = stuff;
}
}
// simple stuff class with everything public for demonstration only
public class Stuff {
public String name;
public int count;
public int distinctCount;
}
To use it you can just do:
new ObjectMapper().readValue(myJson, Wrapper.class);
For the other way around you can use #JsonAnyGetter which should return a Map<String, List<Stuff>) in this case.
I think the easiest solution is to use a custom JsonDeserializer. It allows you to parse the input step by step and to extract only those information you need to build your object.
Here is a simple example how to implement a custom deserializer: custom jackson deserializer

JSON object mapper: avoid listing derived class in serialized JSON string

Here is my class structure:
public class Business {
public long id;
public List<Employee> employees;
// constructors, getters, setters
}
public class BusinessParcelable extends Business implements Parcelable {
// some functions, not additional fields
}
public class Employee {
// fields, constructor, getters, setters
}
public class EmployeeParcelable extends Employee implements Parcelable {
// some functions, no additional fields
}
If I serialize it into a JSON string using the following code:
someFunction(BusinessParcelable b) {
ObjectMapper objectMapper = new ObjectMapper();
Business base = (Business)b;
String jsonString = objectMapper.writeValueAsString(base);
}
The jsonString looks like:
{
"id": 15,
"employees": [
{
...
}
],
"employeesParcelable": [
{
...
}
]
}
I do not want employees and employeesParcelable duplicated in my JSON string. I am able to solve it by replacing:
Business base = (Business)b;
With:
Business base = new Business();
base.setXXX(b.getXXX());
base.setYYY(b.getYYY());
base.setZZZ(b.getZZZ());
But I want to avoid this deep copy as my class structure has a lot of fields and is multiple levels deep. Is there are way in jackson to avoid duplicating the base and derived list (through some simple annotations)?
Edit:
I was just able to get the desired result using Google Gson:
Gson gson = new Gson();
String jsonUsingGson = gson.toJson(base);
It would still be good to know how to get the same result using Jackson though.
I think, that your Parcelable interface contains getEmployeesParcelable method. By default Jackson serializes all getters. To avoid this, you can annotate your method in this way:
interface Parcelable {
#JsonIgnore
List<Employee> getEmployeesParcelable();
//other methods
}

Categories

Resources