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>>() {});
Related
I am new to Avro serialization. My requirement is to serialize a class having a Map<String, Object> as its property. This object could be any type of class like Student, Teacher, Principal.
class Payload
{
private string transactionNo,
private Map<String,Object> map,
}
Please suggest to me a way to write a schema for the above scenario.
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.
I am trying to find an easy way to deserialize the following JSON to an OpeningHours Object containing a Map<String, List<String>> which contains the days as keys and the opening hours as a list.
I am using Jackson and created my own deserializer #JsonDeserialize(using = MyDeserializer.class)
The problem is that I need to check which of the different JSON nodes is present to set the keys for the map.
Is there an easy alternative solution?
{
"OpeningHours": {
"Days": {
"Monday": {
"string": [
"09:00-13:00",
"13:30-18:00"
]
},
"Tuesday": {
"string": [
"09:00-13:00",
"13:30-18:00"
]
}
}
}
}
Jackson can deserialize any Json into Map<String, Object>, that may contain nested maps for nested json objects. all that is needed is casting:
String json = ...
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> openingHours = (Map<String, Object>)mapper.readValue(json, Map.class);
Map<String, List<String>> days = (Map<String, List<String>>)((Map<String, Object>)openingHours.get("OpeningHours")).get("Days");
System.out.println(days);
output:
{Monday={string=[09:00-13:00, 13:30-18:00]}, Tuesday={string=[09:00-13:00, 13:30-18:00]}}
You could just deserialize it to a data structure that represents the JSON like
#Data
public class TempStore {
private List<DayTempStore> days;
}
#Data
public class DaytempStore {
private String[] string;
}
and just transform this to a Map> leaving the hassle with Nodes and Checks to Jackson.
I think you are making the issue is a bit more complex than it is. You don't need a custom deserializer in this case. All you need is this:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModules(new JavaTimeModule());
ObjectReader obrecjReader = objectMapper.reader();
Map<String, Object> myMap = objectReader.forType(Map<String,Object>.class).readValue(jsonString);
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);
Assume, I have the following class:
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ObjectOfMonitoring {
private BigInteger id;
private Map<String, Object> properties = new HashMap<>();
#JsonAnySetter
public void add(String key, Object value) {
properties.put(key, value);
}
#JsonAnyGetter
public Map<String, Object> getProperties() {
return properties;
}
I test it in the following code:
ObjectOfMonitoring objectOfMonitoring = new ObjectOfMonitoring();
objectOfMonitoring.setId(BigInteger.ONE);
objectOfMonitoring.add("key1", "value1");
String jsonInString = mapper.writeValueAsString(objectOfMonitoring);
System.out.println(jsonInString);
I want to get result:
{"id":1,"key2":"value2","key1":"value1"}
But actual result is:
{"id":1,"properties":{"key2":"value2","key1":"value1"}}
What do I do incorrectly? And how to get the expected result?
Make sure that ObjectMapper and #JsonAnySetter/#JsonAnyGetter annotations are from the same packages.
All should be:
either from org.codehaus.jackson - which is the older
version of jackson
or from com.fasterxml.jackson - which
is newer (Jackson has moved from Codehaus when releasing Jackson 2 -
see here)
If you have both dependencies in your project and you are using them interchangeably you may have such hard to notice problems.
The best would be to just get rid of org.codehaus.jackson from your project dependencies.
Might be late to the party but thought of posting the answer as it can be helpful to someone in the future.
This is the sample code which you can configure accordingly for your class.
The main class which will read the JSON and associates with the class:
public class Main {
public static void main(String[] args) throws Exception {
File jsonFile = new File("test.json").getAbsoluteFile();
final ObjectMapper mapper = new ObjectMapper();
MyObject myObject = mapper.readValue(jsonFile, MyPojo.class);
}
}
Your custom POJO:
class MyObject {
private int id;
private Map<String,String> customField;
//Getter and Setter for POJO fields ommited
#JsonAnySetter
public void setCustomField(String key, Object value) {
System.out.println(" Key : " + key + " Value : " + value);
if(customField == null){
customField = new HashMap<String,Object>();
}
customField.put(key,value);
}
#JsonAnyGetter
public Map<String,String> getCustomField(){
return customField;
}
}
This will work if even for duplicate keys within the JSON if you are using the readValue from ObjectMapper but if you are using the treeToValue method from ObjectMapper then this would fail for the duplicate keys and you will have only the last value for the duplicated field. I am still figuring out a way to access the duplicate field while using the treeToValue method