Using Jackson to deserialize JSON to Map - java

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);

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.

How to create Map from yaml file with Jackson?

This is the class:
public class YamlMap {
Map<String, String> mp = new HashMap<>();
String get(String key) {
return this.mp.get(key);
}
}
and this is the props.yml:
mp:
key1: ok
key2: no
When I run:
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
mapper.findAndRegisterModules();
YamlMap ym2 = mapper.readValue(new File("src/main/resources/props.yml"), YamlMap.class);
then I get error:
Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "YamlMap" (class YamlMap)
A quick solution is to add #JsonProperty("mp") above your field :
public class YamlMap {
#JsonProperty("mp")
Map<String, String> mp;
}
Jackson core annotation names might be misleading but even though this annotation has Json in its' name - it will work. Jackson can be configured to parse different formats like Yaml or CBOR - but still for mapping you would use core annotations which have Json in their names.
Another solution is to create a constructor and use #JsonCreator :
public class YamlMap {
Map<String, String> mp;
#JsonCreator
public YamlMap(#JsonProperty("mp") Map<String, String> mp) {
this.mp = mp;
}
}

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>>() {});

How to force JACKSON to use JSON Array when converting/serializing Java Arraylist?

I have a class that I am trying to serialize using Jackson APIs. The sample class looks like this:
public class Pojo {
String var0;
Map<String, String> var1;
public String toJson() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(this);
}
}
When I run this code with more than one entries in map, code works fine by generating output like: {"var0":"blah","var1":[{"blah","blah"},{"blah","blah"}]}
However, when the map has only one item, resulting JSON looks something like: {"var0":"blah","var1":{"blah","blah"}}
How can I force Jackson to generate something like: {"var0":"blah","var1":[{"blah","blah"}]}?
Try changing
Map<String, String> var1;
to
List<Map<String, String>> var1;

Jackson JSON serialization in Java - write k/v pairs to root instead of nested in property name

I need to write a JSON string that follows this basic format:
{
"xmlns": {
"nskey1" : "nsurl1",
"nskey2" : "nsurl2"
},
"datakey1": "datavalue1",
"datakey2": "datavalue2"
}
I'm using the following class to present the data, and an instance of this class is serialized with the Jackson ObjectMapper.
public class PayloadData {
public Map<String, String> payloadData = new TreeMap<String, String>();
#JsonProperty("xmlns")
public Map<NamespaceEnum, String> namespaces = new TreeMap<NamespaceEnum, String>();
#JsonAnyGetter
public Map<String, String> getPayloadData() {
return payloadData;
}
}
If I serialize an instance of this class as-is, the resulting JSON will be something like this:
{
"xmlns": {
"nskey1" : "nsurl1",
"nskey2" : "nsurl2"
},
"payloadData": {
"datakey1": "datavalue1",
"datakey2": "datavalue2"
},
"datakey1": "datavalue1",
"datakey2": "datavalue2"
}
That makes sense based on the naming conventions, but I'm looking for a method to have the payloadData map placed in the JSON's root context without the duplicate that contains the property identifier and nesting. I've tried a lot of annotations in various forms; I've tried disabling the ObjectMapper wrap_root_value SerializationFeature; I honestly feel like I've tried just about everything. So before I throw a computer out the window, I'm asking for a second (and beyond) set of eyes to help point out what must be painfully obvious.
Thanks in advance for your attention and advice.
edit: updated the actual output JSON I see now. The data is being duplicated, and I'd like to remove the duplicate that has the nested property.
The problem is that you have 2 accessors exposed for PayloadData: the public property and the getter, so it is being serialized twice. If it is possible, I would recommend restructuring your data class to be immutable. For example:
public class PayloadData {
private final Map<String, String> payloadData;
private final Map<NamespaceEnum, String> namespaces;
#JsonCreator
public PayloadData(#JsonProperty("xmlns") Map<NamespaceEnum, String> namespaces,
#JsonProperty("payloadData") Map<String, String> payloadData) {
this.namespaces = namespaces;
this.payloadData = payloadData;
}
#JsonAnyGetter
public Map<String, String> getPayloadData() {
return payloadData;
}
#JsonProperty("xmlns")
public Map<NamespaceEnum, String> getNamespaces() {
return namespaces;
}
}
This will give you the desired output with out any configuration of the ObjectMapper.
You can parse your json data to a HashMap not a class object:
public HashMap<String, Object> testJackson(String data) throws IOException {
JsonFactory factory = new JsonFactory();
ObjectMapper mapper = new ObjectMapper(factory);
TypeReference<HashMap<String,Object>> typeRef
= new TypeReference<HashMap<String,Object>>() {};
HashMap<String,Object> o = mapper.readValue(data, typeRef);
return o
}
Get JSON data from a HashMap:
public String getJsonFromMap(HashMap<String, Object> data) {
return new ObjectMapper().writeValueAsString(data);
}

Categories