JsonAnyGetter / JsonAnySetter the resulting JSON has doubled values - java

I need to write a JSON string that follows this basic format:
"gesamtAngebot":{
"angebotList":[{
"instanzId":"string",
"buchungsKontextList":[{
"quellSystem":"SOMETHING",
"payload":{}
}],
"payload":{"test1":"test1"}
}]
}
I'm using the following class to present the data, and an instance of this class is serialized with the Jackson ObjectMapper.
#Data
public class Angebot {
private String instanzId;
private List<BuchungsKontext> buchungsKontextList;
private Map<String, Object> payload = new HashMap<String, Object>();
#JsonAnyGetter
public Map<String, Object> any() {
return payload;
}
#JsonAnySetter
public void set(String name, Object value) {
payload.put(name, value);
}
}
If I serialize an instance of this class as-is, the resulting JSON will be something like this:
"gesamtAngebot":{
"angebotList":[{
"instanzId":"string",
"buchungsKontextList":[{
"quellSystem":"SOMETHING",
"payload":{}
}],
"payload":{"test1":"test1"},
"test1":"test1"
}]
}
As you can see the data of "payload" is doubled as it's own element and I don't have any idea why.
Thanks in advance for your attention and advice.

It seems like you want to serialize payload as a normal map. So if you don't want it there twice then you should not have the any() method, just have a regular getter method for payload.
The any() method can be used in case you want to serialize all the items from the payload map to appear like they are properties of Angebot class. Then you would use the any method, and not have a getter for payload.
Your JSON would come out like this:
"gesamtAngebot":{
"angebotList":[{
"instanzId":"string",
"buchungsKontextList":[{
"quellSystem":"SOMETHING",
"payload":{}
}],
"test1":"test1"
}]
}
And it will look like test1 is a variable of the Angebot class.

It is because of any() getter. Just remove it:
#Data
public class Angebot {
private String instanzId;
private List<BuchungsKontext> buchungsKontextList;
private Map<String, Object> payload = new HashMap<String, Object>();
// #JsonAnyGetter
// public Map<String, Object> any() {
// return payload;
// }
#JsonAnySetter
public void set(String name, Object value) {
payload.put(name, value);
}
}
payload is class property. It gets naturally de-serialized because of #Data annotation. any() getter creates duplicity.

Related

Custom JSON Serialization Wrapper for Any Object

I use external application which expects an Object that Serializable from me like his function:
externalFunction(Object input);
So I should give that function an input that will be correctly serialized into JSON when the method is invoked (not controlled by me).
But I don't know how data is structured since I receive input from another external application dynamically. So case like this:
1. Get data from 3rd party
2. MyApp should annotate data for Json Serialization
3. Send data to 3rd party as input
4. Response will be produced as JSON
How can I achieve this? How can I give input to the function that is correctly serialized when the function is invoked?
What I tried so far:
So first thing I try is wrap data with some Wrapper like:
public class JsonWrapper<T> implements Serializable
{
public T attributes;
public JsonWrapper( T attributes )
{
this.attributes = attributes;
}
#JsonValue
public T getAttributes( )
{
return attributes;
}
}
So I wrap data like ->
data = getFromThirdParty();
wrapped = new JsonWrapper<>(data);
externalFunction(wrapped);
But it produces a response with "attributes" field which I don't want. Also I tried to use #JsonUnwrapped public T attributes; but the result is same.
I don't want this:
{
"attributes": {
... some fields/values that I don't know, get from 3rd party
}
}
I want like this:
{
... some fields/values that I don't know, get from 3rd party
}
The #JsonUnwrapped annotation doesn't work when T is a Collection (see this answer from the Jackson's creator). But the #JsonValue annotation actually does the trick:
public class JsonWrapper<T> {
#JsonValue
private T value;
public JsonWrapper(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
If you use Lombok, you can have:
#Getter
#AllArgsConstructor
public class JsonWrapper<T> {
#JsonValue
private T value;
}
Example
Consider the following class:
#Data
#AllArgsConstructor
public class Person {
private String firstName;
private String lastName;
}
When serializing an Person instance, the following result JSON is produced:
ObjectMapper mapper = new ObjectMapper();
JsonWrapper<?> wrapper = new JsonWrapper<>(new Person("John", "Doe"));
String json = mapper.writeValueAsString(wrapper);
{"firstName":"John","lastName":"Doe"}
When serializing a list of Person instances, the following result JSON is produced:
ObjectMapper mapper = new ObjectMapper();
JsonWrapper<?> wrapper = new JsonWrapper<>(
Arrays.asList(
new Person("John", "Doe"),
new Person("Jane", "Poe")
));
String json = mapper.writeValueAsString(wrapper);
[{"firstName":"John","lastName":"Doe"},{"firstName":"Jane","lastName":"Poe"}]

Can Jackson serialize to JSON with customizable key name?

Is it possible to dynamically customize the key names in the JSON response at runtime, rather than creating individual POJO classes for domain level objects?
I am using Spring Boot 1.5.3 with Web Starter, so Jackson dependency is included. I am returning responses in JSON. Typically, I create individual POJO classes, with Jackson annotations if I need to customize key names. For example,
public class Movies {
private List<String> movies;
public Movies(List<String> movies) {
this.movies = movies;
}
public List<String> getMovies() {
return this.movies;
}
public void setMovies(List<String> movies) {
this.movies = movies;
}
}
When I am returning this from a #RestController with the following code:
#RestController
public class MoviesController {
#Service
private MovieService movieService;
#RequestMapping("/movies/list")
public ResponseEntity<Movies> getMovies() {
return new ResponseEntity<Movies>(this.movieService.getMovies(), HttpStatus.OK);
}
}
I get back a JSON response when invoking this end-point:
{ "movies" : [ "Iron Man", "Spiderman", "The Avengers", "Captain America" ] }
I don't want to be creating the Movies POJO. Instead, I would like to have a generic-typed POJO:
public class GenericResponse {
#JsonProperty("movies") // <- this needs to be dynamic
private List<String> data;
...
}
...where I can somehow send any key name I want while instantiating GenericResponse as opposed to hard-coding the key name via a #JsonProperty annotation. Is that possible?
Replace Movies and GenericResponse with Map<String, List<String>>, then do
map.put("movies", Arrays.asList("Iron Man", "Spiderman", "The Avengers", "Captain America"));
A Map is serialized to JSON as a JSON Object, with the map keys as field names, and map values and field values.
What about doing this through Map ?
public class GenericResponse {
#JsonValue
private Map<String, List<String>> data;
}
and you can use #JsonValue annotation to ignore the "data" field name !

#jsonanygetter doesn't work

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

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

Saving UnknownFields to a map while deserializing Json using Jackson

My class looks like:
Class A{
private String amount;
#JsonIgnore
private Map<String,String> unknownFields = new HashMap<>();
}
My ObjectMapper have DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES = false configured.
Json input:
{
"amount": 5000,
"note" : "Amount is 5000"
}
In this case I need the note to be in the unknownFields Map:
I am looking for some annotations like
#OnUnknownProperties
public void OnUnknownProperties(String name, String value){
unknownFields.put(name,value);
}
You could annotate a Method in your Domain-Class with #JsonAnySetter (#JsonAnyGetter) and handle it. A good example is here:
http://www.jasonwhaley.com/handling-top-level-metadata-with-jackson/ . Let your DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES=false.

Categories