#RequestBody mapping complex json object - java

I need to convert below json to java object of #RequestBody.
{
"entity": {
"id": 3,
"name": "james"
},
"conjunction": "OR",
"conditions": [
{
"operation": "equalTo",
"dataKey": "department",
"dataType": "string",
"value": "abc"
},
{
"operation": "notEqualTo",
"dataKey": "ID",
"dataType": "number",
"value": "100"
},
{
"operation": "notEqualTo",
"dataKey": "name",
"dataType": "strubg",
"value": "jack"
},
{
"operation": "between",
"dataKey": "END_DATE",
"dataType": "date",
"value1": "20180502",
"value2": "20180519"
}
]
}
The first three element in the array correspond to below java object.
public class ComparisonCondition extends Condition {
private String value;
}
The last element correspond below object.
public class BetweenCondition extends Condition {
private String value1;
private String value2;
}
They all inherit from below object.
public class Condition {
private String dataKey;
private String dataType;
private String operation;
}
The spring mvc method is below.
#RequestMapping(value = RequestAction.FILTER, method = RequestMethod.POST)
public List<Student> filter(
#RequestBody Filter<Student> filterConfig) {
return null;
}
The Filter object is below.
public class Filter<T> {
private String conjunction;
private T entity;
private List<Condition> conditions;
}
How can I map the json to java object successfully?
Currently it report "Could not read JSON: Unrecognized field "value" (class com.ssc.rest.entity.Condition), not marked as ignorable (3 known properties: "dataType", "dataKey", "operation"])

For your error, if the jackson parser don't know a field, it throws an exception.
You can avoid it by putting the annotation :
#JsonIgnore(ignoreUnknown=true)
on the target object.
For your mapping, I recommand to you to create an object corresponding to your json input, and then do manually your mapping to your target objects.

You are passing 4 variables in JSON for COndition
{
"operation": "equalTo",
"dataKey": "department",
"dataType": "string",
"value": "abc"
},
but your Java POJO has only 3 variables
public class Condition {
private String dataKey;
private String dataType;
private String operation;
}
just add value as well it will work fine.
Bottom line is : POJO class should have all the fields passed in JSON.
By the way your exception is telling same thing
Unrecognized field "value"
Edit 1:
I missed BetweenCondition and ComparisonCondition
You can define the Base Class in your case Condition with Sub Class property and hopefully it should work
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "javaclass")
#JsonSubTypes({
#Type(value = ComparisonCondition.class),
#Type(value = BetweenCondition.class)
})
public class Condition {
private String dataKey;
private String dataType;
private String operation;
}

Related

Error parsing JSON (MismatchedInputException)

I'm having problems parsing JSON, this is the error:
out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList<packagename....>` out of START_OBJECT token
And I know why it is happening I just don't know how to fix it. This JSON works:
{
"status_code": "SUCCESS",
"time": {
"date": "Mar 23, 2021 1:14:39 AM"
},
"info": [
{
"person": "2.2",
"role": "TEACHER"
},
{
"person": "2.3",
"role": "TEACHER"
}
]
}
This one does not:
{
"status_code": "SUCCESS",
"time": {
"date": "Mar 23, 2021 3:49:27 AM"
},
"info": {
"id": "1",
"person": [
{
"identifier": "John",
"role": "TEACHER"
},
{
"identifier": "Homer",
"role": "TEACHER"
},
{
"identifier": "Michael",
"role": "TEACHER"
},
{
"identifier": "Sarah",
"role": "TEACHER"
}
]
}
}
The problem seems to be the { character in front of the info field because with [ works. So this is the method I'm using to parse the JSON:
public Mono<PersonResponse> searchById(String id) {
return webClient.get().uri(id).retrieve().bodyToMono(PersonResponse.class);
}
Also tried:
public Mono<PersonResponse[]> searchById(String id) {
return webClient.get().uri(id).retrieve().bodyToMono(PersonResponse[].class);
}
Error right on line: 1, column: 1. Any suggestions of how to implement the method?
EDIT: Added classes.
PersonResponse:
public class PersonResponse implements Serializable{
private static final long serialVersionUID = 7506229887182440471L;
public String status_code;
public Timestamp time;
public List<PersonDetails> info;
public PersonResponse() {}
...getters / setters / toSting
PersonDetails:
public class PersonDetails implements Serializable{
private static final long serialVersionUID = 1294417456651475410L;
private int id;
private List<Person> person;
public PersonDetails(int version) {
super();
this.version = version;
}
...getters / setters / toSting
Person
public class Person implements Serializable{
private static final long serialVersionUID = 3290753964441709903L;
private String identifier;
private String role;
public Person(String identifier, String role) {
super();
this.identifier = identifier;
this.role = role;
}
...getters / setters / toSting
The problem isn't the JSON necessarily, it's that the JSON structure doesn't match your PersonResponse class. There's an info variable in PersonResponse that requires an array of what I assume to be persons, in the second example you're trying to push an object in there, which you can't. You have to either change your JSON, which you don't seem to want in this case, or the class you're trying to parse it to.
You need to restructure the info variable in PersonResponse to match the object you're trying to parse to it.

JSON parse error: Can not deserialize instance - Java

I'm creating a REST service that receives a JSON input like this:
[
{
"person": {
"name": "string",
"surname": "string",
"address": "string",
"age": "data",
"info": {
"number": "string"
}
}
},
{
"person": {
"name": "string",
"surname": "string",
"address": "string",
"age": "data",
"info": {
"number": "string"
}
}
}
]
My items are(
I omitted getters and setters):
public class Request {
private List<Person> person;
}
public class Person{
private String name;
private String surname;
private String address;
private XMLGregorianCalendar age;
private Info info;
}
public class Info {
private String number;
}
how come i get the following error?
{
"timestamp": 1611142052198,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.http.converter.HttpMessageNotReadableException",
"message": "JSON parse error: Can not deserialize instance of com.myproject.model.Request out of
START_ARRAY
}
i need the json structure to be exactly that.
You are trying to de-serialize your input array to a Request object, which is not a collection, and thus you get that error.
To solve it you should de-serialize your input array to a List<Person> object and then set that object to your Request object:
Request request = ... // get the request
List<Person> person = ... // deserialize the input array
request.setPerson(person);
If you are using Jackson, you can also define a constructor for your Request class which takes a List<Person> argument and then mark that constructor with the #JsonCreator annotation (see here for an example):
public class Request {
private List<Person> person;
#JsonCreator
public Request(#JsonProperty("person") List<Person> person) {
this.person = person;
}
}
And then you can create your Request object directly from your input array.
By using the #JsonCreator annotation you keep your Request class immutable, because you don't need to define a setPerson(List<Person>) method for it.

JACKSON: How to ignore the POJO name while converting a POJO to JSON using Jackson?

I am using Jackson 2.10.1 library to convert my Java POJOs to JSON and I am getting the below output, I require the output without the POJO name(MyTestPojo here), I have tried various jackson annotations like #JsonIgnoreProperties but those are mostly for the members present in the POJO and not the POJO class name.
{
"MyTestPojo": [
{
"CreatedBy": "user1",
"Name": "testABC",
"UpdatedBy": null,
"UpdatedDate": null,
"IsActive": true,
"Value": "testABC1",
"CreatedDate": "2017-03-13 15:41:54.0",
"Description": "testABC"
},
{
"CreatedBy": "user2",
"Name": "testABC",
"UpdatedBy": null,
"UpdatedDate": null,
"IsActive": false,
"Value": "testABC2",
"CreatedDate": "2017-03-13 15:41:54.0",
"Description": "testABC"
}
]
}
whereas what I require is -
[
{
"CreatedBy": "user1",
"Name": "testABC",
"UpdatedBy": null,
"UpdatedDate": null,
"IsActive": true,
"Value": "testABC1",
"CreatedDate": "2019-03-13 15:41:54.0",
"Description": "testABC"
},
{
"CreatedBy": "user2",
"Name": "testABC",
"UpdatedBy": null,
"UpdatedDate": null,
"IsActive": false,
"Value": "testABC2",
"CreatedDate": "2020-03-10 15:41:54.0",
"Description": "testABC"
}
]
}
Is there a way to handle this with Jackson annotations?
The POJOs that I have used are-
#JacksonXmlRootElement(localName = "ArrayOfTestPojos")
public class GetResponseVO {
#JsonProperty("MyTestPojo")
#JacksonXmlProperty(localName = "MyTestPojo")
#JacksonXmlElementWrapper(useWrapping = false)
private ArrayList<MyTestPojo> MyTestPojoList;
public ArrayList<MyTestPojo> getMyTestPojoList() {
return MyTestPojoList;
}
public void setMyTestPojoList(ArrayList<MyTestPojo> MyTestPojoList) {
this.MyTestPojoList = MyTestPojoList;
}
// standard getters and setters
}
and
#JacksonXmlRootElement(localName = "MyTestPojo")
public class MyTestPojo {
#JsonProperty("Name")
private String name;
#JsonProperty("Description")
private String description;
#JsonProperty("IsActive")
private int isActive;
#JsonProperty("Value")
private String value = null;
#JsonProperty("CreatedBy")
private String createdBy;
#JsonProperty("CreatedDate")
private String createdDate;
#JsonProperty("UpdatedBy")
private String updatedBy;
#JsonProperty("UpdatedDate")
private String updatedDate;
// standard getters and setters.
}
```````````
I am also generating the XML out of this so you can ignore the annotations relevant to XML.
you can use JsonValue annotation for that purpose which basically "use-value of this property instead of serializing the container object". it can be used on getters also
#JsonValue indicates that results of the annotated "getter" method (which means signature must be that of getters; non-void return type, no args) is to be used as the single value to serialize for the instance. Usually value will be of a simple scalar type (String or Number), but it can be any serializable type (Collection, Map or Bean).
#JsonValue
#JacksonXmlProperty(localName = "MyTestPojo")
#JacksonXmlElementWrapper(useWrapping = false)
private ArrayList<MyTestPojo> MyTestPojoList;
But that would wrong practice as it will generate JSON like this, which would not be legal JSON.
{[{"x":"value"}, ...]}
If you want to alter only JSON structure (without affecting xml), you can use MixIn for that purpose.
public interface JsonMixin {
#JsonValue
List<MyTestPojo> getMyTestPojoList();
}
And register it with your object mapper and remove #JsonValue from the main Class.
objectMapper.addMixIn(GetResponseVO.class, JsonMixin.class);

Java Jackson deserialize objects of the same name but different class types

I have POJOs that are used as the request and response object in a REST API like so (I know duplicate #JsonProperty isn't syntactically correct, see below):
public class Request {
#JsonProperty("patient")
PatientObjectA patientA;
#JsonProperty("patient")
PatientObjectB patientB;
}
public class PatientObjectA {
#JsonProperty("identifier")
Private Identifier identifier
#JsonProperty("system")
Private String system;
#JsonProperty("value")
Private String value;
}
public class PatientObjectA {
#JsonProperty("identifier")
Private List<Identifier> identifier
#JsonProperty("system")
Private String system;
#JsonProperty("value")
Private String value;
}
There are minor differences in cardinality in that I want to be able to consume i.e the "Patient" object will sometimes be (PatientObjectA in Request class):
"patient": {
"identifier": {
"type": {
"coding": {
"system": "NA",
"code": "Patient"
},
"text": "Patient"
},
"system": "Patient",
"value": "000000000"
}
}
or this case (note the differences in cardinality on the identifier object, where in this case identifier can have one or more items) (PatientBObject in Request class):
"patient": {
"identifier": [{
"type": {
"coding": {
"system": "NA",
"code": "Patient"
},
"text": "Patient"
},
"system": "Patient",
"value": "3018572032"
}]
}
I would like to achieve a functionality where requests are mapped to the correct objects. Is there a way (other than a custom deserializer) where I can map the requests to the appropriate object by type/cardinality? Any insight would be appreciated!
Jackson support this with the #JsonTypeInfo annotation.
I recommend specifying the type info in a property (a json field) and use the full class name (as opposed to a short name) to provide a better guarantee of uniqueness:
#JsonTypeInfo(include = JsonTypeInfo.As.PROPERTY, use = JsonTypeInfo.Id.CLASS, property = "jsonType")
public class PatientObjectA {
..
Output A looks like:
"patient": {
"jsonType": "com.company.PatientAObject"
"identifier": {
"type": {
"coding": {
"system": "NA",
"code": "Patient"
},
"text": "Patient"
},
"system": "Patient",
"value": "000000000"
}
}
Output B looks like:
"patient": {
"jsonType": "com.company.PatientBObject"
"identifier": {
"type": {
"coding": {
"system": "NA",
"code": "Patient"
},
"text": "Patient"
},
"system": "Patient",
"value": "000000000"
}
}
Note: Also, check out #JsonRootName, as it will give you the ability to create a 'rooted' json object without having to have that wrapper object you have.
#JsonRootName("Patient")
#JsonTypeInfo(include = JsonTypeInfo.As.PROPERTY, use = JsonTypeInfo.Id.CLASS, property = "jsonType")
public class PatientObjectA {
..
.. and ..
#JsonRootName("Patient")
#JsonTypeInfo(include = JsonTypeInfo.As.PROPERTY, use = JsonTypeInfo.Id.CLASS, property = "jsonType")
public class PatientObjectB {
..
Related terms to assist with more research:
polymorphism in json
json equivalent of xml namespaces.

JSON generic collection deserialization

I've such DTO classes written in Java:
public class AnswersDto {
private String uuid;
private Set<AnswerDto> answers;
}
public class AnswerDto<T> {
private String uuid;
private AnswerType type;
private T value;
}
class LocationAnswerDto extends AnswerDto<Location> {
}
class JobTitleAnswerDto extends AnswerDto<JobTitle> {
}
public enum AnswerType {
LOCATION,
JOB_TITLE,
}
class Location {
String text;
String placeId;
}
class JobTitle {
String id;
String name;
}
In my project there is Jackson library used for serialization and deserialization of JSONs.
How to configure AnswersDto (use special annotations) or AnswerDto (annotation as well) classes to be able to properly deserialize request with AnswersDto in its body, e.g.:
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"answers": [
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"type": "LOCATION",
"value": {
"text": "Dublin",
"placeId": "121"
}
},
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"type": "JOB_TITLE",
"value": {
"id": "1",
"name": "Developer"
}
}
]
}
Unfortunately Jackson by default maps value of AnswerDto object to LinkedHashMap instead of object of proper (Location or JobTitle) class type.
Should I write custom JsonDeserializer<AnswerDto> or configuration by use of #JsonTypeInfo and #JsonSubTypes could be enough?
To properly deserialize request with just one AnswerDto in form of
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"type": "LOCATION",
"value": {
"text": "Dublin",
"placeId": "121"
}
}
I'm using:
AnswerDto<Location> answerDto = objectMapper.readValue(jsonRequest, new TypeReference<AnswerDto<Location>>() {
});
without any other custom configuration.
I've resolved issue by using Jackson's custom annotations #JsonTypeInfo and #JsonSubTypes:
public class AnswerDto<T> {
private String uuid;
private AnswerType type;
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = Location.class, name = AnswerType.Types.LOCATION),
#JsonSubTypes.Type(value = JobTitle.class, name = AnswerType.Types.JOB_TITLE)
})
private T value;
}
My suggestion is to make a separate interface for possible answer values and use #JsonTypeInfo on it. You can also drop type field from AnswerDto, AnswerType enum, and additional *AnswerDto classes becuse jackson will add type info for you. Like this
public class AnswerDto<T extends AnswerValue> {
private String uuid;
private T value;
}
#JsonTypeInfo(use = Id.CLASS, include = As.PROPERTY)
interface AnswerValue {}
class Location implements AnswerValue { /*..*/ }
class JobTitle implements AnswerValue { /*..*/ }
Resulting json will looks like this
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"answers": [
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"value": {
"#class": "com.demo.Location",
"text": "Dublin",
"placeId": "121"
}
},
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"value": {
"#class": "com.demo.JobTitle",
"id": "1",
"name": "Developer"
}
}
]
}
Which will be parsed using
AnswersDto answersDto = objectMapper.readValue(json, AnswersDto.class);
But this solution applies only in cases when you are a producer of json data and you do not have to think about backward compatibility.
In other cases you'll have to make custom desetializer for AnswersDto class.

Categories