java convert map to pojo ignore some keys - java

I have a map that unsure it keys,but I am sure the keys contains all the pojo fields, says:
public class MyPojo{
String name,
String addr
}
//map contains keys that not in MyPojo field,e.g. age
map = {"name":"john","addr":"sf school","age":"21"}
In Java how can I can convert it to pojo MyPojo instance? the following method throw exception:
final ObjectMapper mapper = new ObjectMapper(); // jackson's objectmapper
final MyPojo pojo = mapper.convertValue(map, MyPojo.class);

You can use #JsonIgnoreProrperties by Jackson in your MyPojo class.
The exception is cause of ObjectMapper not being able to find exact mapping in your MyPojo class.
The API is provided in the same library, that of ObjectMapper.
So here is what your class should look like:
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyPojo{
String name;
String addr;
//Other variables
}
To import it in your code, you need to add the following:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
I hope this solves your problem and thisis exactly what you're looking for.

As you are working on generics keys(previously unknown), there might be chances of keys(attribute names) present in the map as a key doesn't present in POJO as an attribute. So, set below properties to false, when you create an ObjectMapper instance, so that any unknown or missing attributes wouldn't throw any exceptions.
final ObjectMapper mapper = new ObjectMapper(); // jackson's objectmapper
mapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

Related

Java Jackson ObjectMapper - Annotation for taking an object as is without digging deeper

I have a simple POJO class looking like this:
public class EventPOJO {
public EventPOJO() {
}
public String id, title;
// Looking for an annotation here
public Timestamp startDate, endDate;
}
This is how I create a Map using ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> event = mapper.convertValue(myPOJO, new TypeReference<Map<String, Object>>() {});
I need mapper to contain the key "startDate" with the value of Type Timestamp.
(com.google.firebase.Timestamp)
Instead however mapper contains the key "startDate" with the value of Type LinkedHashMap containing the nanoseconds and seconds.
So basically I need the mapper to stop when seeing an object of type Timestamp and put it in the map as is.

JsonNullable is not serializing its value with Jackson

I'm trying to use JsonNullable<String> to distinguish between the absence of a value and null. When I serialize my Java object, User, the JsonNullable<String> field is serialized as a JSON object with value
{"present":false}
I'm expecting it to print {} since I initialized the field with undefined.
Here's my class
public class User {
#JsonProperty("userId")
private JsonNullable<String> userId = JsonNullable.undefined();
//set and get
//tostring
}
and a small driver program where I actually set a value within the JsonNullable field.
User user = new User();
user.setUserId(JsonNullable.of("12345"));
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());
String expectedData = objectMapper.writeValueAsString(user);
System.out.println(expectedData);
This prints
{"userId":{"present":true}}
But I expected
{"userId":"12345"}
In other words, I expected the value wrapped within the JsonNullable to be serialized into the JSON. Why are JsonNullable and Jackson behaving this way?
As the wiki instructs, you need to register the corresponding module (in addition to any others you have):
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.registerModule(new JsonNullableModule());
Add the following configuration in Spring Boot:
#Configuration
public class JacksonConfiguration {
#Bean
public JsonNullableModule jsonNullableModule() {
return new JsonNullableModule();
}
}
Registering the JsonNullableModule is the correct solution. But I just want to point out that JsonNullable doesn't play well with openapi-generator. We generate client APIs from the backend controller annotations. The required, nullable annotations on #schema didn't work for us (on a Kotlin data class). We went back with using Optional<T>? and worked out perfectly with out any extra annotations needed.

Importing some values from hashmap to POJO

I want to convert a Hashmap of type to a POJO. I am using jackson to convert it currently, however, since the request is a API request, the user might provide more fields than needed.
For example,
The hashmap could be :
{
field1ID:"hi",
field2ID:["HI","HI","JO"],
field3ID:"bye"
}
while the pojo is simply
{
field1ID:"hi",
field3ID:"bye"
}
When using ObjectMapper.convertValue, unless there is a one to one mapping from hashmap to pojo, a IllegalArguemetException will be throw. What I wan to do is, if the field is there then map the field. Else leave it as null.
As you didn't provide any code in your question, consider, for example, you have a map as shown below:
Map<String, Object> map = new HashMap<>();
map.put("firstName", "John");
map.put("lastName", "Doe");
map.put("emails", Arrays.asList("johndoe#mail.com", "john.doe#mail.com"));
map.put("birthday", LocalDate.of(1990, 1, 1));
And you want to map it to and instance of Contact:
#Data
public class Contact {
private String firstName;
private String lastName;
private List<String> emails;
}
It could be achieved with the following code:
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
Contact contact = mapper.convertValue(map, Contact.class);
In the example above, the map has a birthday key, which cannot be mapped to any field of the Contact class. To prevent the serialization from failing, the FAIL_ON_UNKNOWN_PROPERTIES feature has been disabled.
Alternatively, you could annotate the Contact class with #JsonIgnoreProperties and set ignoreUnknown to true:
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class Contact {
...
}
And then perform the conversion:
ObjectMapper mapper = new ObjectMapper();
Contact contact = mapper.convertValue(map, Contact.class);
To convert the Contact instance back to a map, you can use:
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.convertValue(contact,
new TypeReference<Map<String, Object>>() {});

Ignore some fields deserialization with jackson without changing model

I'm looking for a way to configure jackson deserializer to ignore some fields. I don't want to achieve this by annotating model since It's out given by another project; I just want to do it by constructing deserializer (ObjectMapper) to do so.
Is it possible?
You can achieve that using Mix-In annotation.
class ThirdPartyReadOnlyClass {
private String ignoredPropertyFromThirdParty;
public String getIgnoredPropertyFromThirdParty() {
return ignoredPropertyFromThirdParty;
}
}
abstract class MixIn {
#JsonIgnore
String getIgnoredPropertyFromThirdParty();
}
You can put json annotations on MixIn class as if you are putting them on original model class.
Configuring object mapper
objectMapper.addMixInAnnotations(ThirdPartyReadOnlyClass.class, MixIn.class);
you have to do following
1)write your own Deserializer which extends JsonDeserializer
2) override deserialize method and return your class object after ignoring some of the fields
3) register your deserializer with ObjectMapper
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(yourClass.class, new yourDerializer());
mapper.registerModule(module);
String newJsonString = "{\"id\":1}";
final yourClass yourClassObject= mapper.readValue(newJsonString, yourClass.class);
Hope this will solve your problem

Exclude empty Arrays from Jackson ObjectMapper

I am building JSON from Java object tree using Jackson ObjectMapper. Some of my Java objects are collections and sometimes they might be empty. So if they are empty that ObjectMapper generates me: "attributes": [], and I want to exclude those kind of empty JSON arrays from my result. My current ObjectMapper config:
SerializationConfig config = objectMapper.getSerializationConfig();
config.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
config.set(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
From this post I've read that I can use:
config.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
But that is generating me an error:
Caused by: java.lang.IllegalArgumentException: Class com.mycomp.assessments.evaluation.EvaluationImpl$1 has no default constructor; can not instantiate default bean value to support 'properties=JsonSerialize.Inclusion.NON_DEFAULT' annotation.
So how should I prevent those empty arrays to appear in my result?
You should use:
config.setSerializationInclusion(JsonSerialize.Inclusion.NON_EMPTY);
for Jackson 1 or
config.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
for Jackson 2
A very good example describing :
JsonInclude.Include.NON_NULL
JsonInclude.Include.ABSENT
JsonInclude.Include.NON_EMPTY
In : https://www.logicbig.com/tutorials/misc/jackson/json-include-non-empty.html
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Employee {
private String name;
private String dept;
private String address;
private List<String> phones;
private AtomicReference<BigDecimal> salary;
.............
}
public class ExampleMain {
public static void main(String[] args) throws IOException {
Employee employee = new Employee();
employee.setName("Trish");
employee.setDept("");
employee.setAddress(null);
employee.setPhones(new ArrayList<>());
employee.setSalary(new AtomicReference<>());
ObjectMapper om = new ObjectMapper();
String jsonString = om.writeValueAsString(employee);
System.out.println(jsonString);
}
}
=====> Result :
If we don't use #JsonInclude annotation at all then output of the above example will be:
{"name":"Trish","dept":"","address":null,"phones":[],"salary":null}
If we use #JsonInclude(JsonInclude.Include.NON_NULL) on Employee class then output will be:
{"name":"Trish","dept":"","phones":[],"salary":null}
If we use #JsonInclude(JsonInclude.Include.NON_ABSENT) then output will be:
{"name":"Trish","dept":"","phones":[]}
If we use #JsonInclude(JsonInclude.Include.NON_EMPTY) :
{"name":"Trish"}
If you can modify the object to be serialized, you can also place an annotation directly on the field, for example (Jackson 2.11.2):
#JsonProperty
#JsonInclude(JsonInclude.Include.NON_EMPTY)
private Set<String> mySet = new HashSet<>();
In this way, no further configuration of the ObjectMapper is required.

Categories