Saving UnknownFields to a map while deserializing Json using Jackson - java

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.

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"}]

JsonAnyGetter / JsonAnySetter the resulting JSON has doubled values

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.

How to make POJO dynamic so that it ignores an json tag but reads the value under that tag using jackson in java?

I've a parent DAO:
#XmlRootElement//(name="metadata")
public class FolderAttributes {
private Map nameValueForListValue;
Child DAO:
#XmlAccessorType(XmlAccessType.FIELD)
public class ListWrapper {
#XmlElementWrapper(name = "attrValue")
private List<Object> list;
JSON request that works (if I use "metadata" name as root element):
"metadata": {
"nameValueForListValue": {
"signed": {
"attrValue": [
"ahsdfhgakjf"
]
},
"id": {
"attrValue": [
"12345678",
"87654321"
]
},
.......... continues
I don't want the tag "nameValueForListValue" in request, instead it should be smart enough to read rest of the values without that tag. Looks like it always needs to have the param name "nameValueForListValue" on the request. Is there any annotations that will do my job easier? I'm using Java 6 & jackson 1.9.
What about using #JsonAnySetter Jackson annotation
It would be something like:
#XmlRootElement//(name="metadata")
public class FolderAttributes {
private Map nameValueForListValue;
#JsonAnySetter
public void genericSetter(String key, Object value){
nameValueForListValue.put(key, value);
}
}
That whay any unknown field could be handle by this setter.
More info:#JsonAnySetter example
#JsonInclude(JsonInclude.Include.NON_NULL)

Jackson Jersey JSON

I'm trying to use Jersey and Jackson (although any other way of doing JSON demarshalling works) to get this into my system in some form (be it POJO or some other representation).
Basically I only need the data section. I was trying to use GenericTypes with lists, but this is a nested list and I'm just not sure what to do. Lots of kudos for help and I really appreciate it!
{
"total": 4,
"data": [
{
"descriptor": "",
"multiInstance": false,
"active": false
},
{
"descriptor": "Apparel",
"multiInstance": true,
},
{
"descriptor": "abcd123",
"multiInstance": false,
},
{
"descriptor": "abcd",
"multiInstance": false,
}
]
}
This is the class I'm trying to use. I need a list of the class.
public class customObject {
#JsonProperty(value = "descriptor")
private String descriptor;
#JsonProperty(value = "multiInstance")
private Boolean multiInstance;
//getters and setters
}
Edit:
I'm using it in here.
CustomObjectResponse WDCOResponse =
resource
.type(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header("Authorization", getToken()).get(WDCOResponse.class);
But it's still not working.
Edit2:
Figured this out! Thanks to everyone. :)
I had to add annotation to tell it to ignore if something wasn't found, some of the JSON I'm getting back was not fully-formed in that not all fields were absolutely neccesary.
If you the object you provided is what you are passing to your controller, then you will need one more wrapper object to contain the list like this:
public class CustomRequest {
#JSonProperty(value = "total");
private Integer total;
#JsonProperty(value = "data")
private List<CustomObject> data;
// getters/setters
}
public class CustomObject {
#JsonProperty(value = "descriptor")
private String descriptor;
#JsonProperty(value = "multiInstance")
private Boolean multiInstance;
// getters/setters
}
Then your controller will just have annotations that show that the RequestBody is the CustomRequest class:
#Controller
public class JSONController {
#RequestMapping(value="sendData")
public #ResponseBody CustomResponse sendData(
#RequestBody CustomRequest request)
{
return null;
}
}
If you are still getting errors, please provide detailed error or problem. Thanks!
You'd use POJO like:
public class Response {
int count;
List<customObject> data;
}
and access the data from there:
for (customObject ob : response.data) {
// process ig
}

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