I have a JSON string which will be of the following format:
{
"response": {
"execution_status": "ready",
"report": {
"cache_hit": true,
"created_on": "2013-07-29 08:42:42",
"fact_cache_error": null,
"fact_cache_hit": true,
"header_info": null,
"name": null,
"report_size": "5871",
"row_count": "33",
"url": "report-download?id=278641c223bc4e4d63df9e83d8fcb4e6"
},
"status": "OK"
}
}
The response part of the JSON is common for a bunch of response types. The report part of this JSON holds good only for this response. So I had created a Response class as shown below with getters and setters (have not included the getters and setters here for brevity):
#JsonRootName(value = "response")
public class Response implements Serializable {
private static final long serialVersionUID = -2597493920293381637L;
#JsonProperty(value = "error")
private String error;
#JsonProperty(value = "error_code")
private String errorCode;
#JsonProperty(value = "error_id")
private String errorId;
#JsonProperty(value = "error_description")
private String errorDescription;
#JsonProperty(value = "method")
private String method;
#JsonProperty(value = "service")
private String service;
#JsonProperty(value = "status")
private String status;
#JsonProperty(value = "execution_status")
private String executionStatus;
}
And then, I created a Report class with the fields in the report element as below. The ReportResponse class will extend from the Response class (again the getters and setters are not included for brevity):
public class ReportResponse extends Response {
private static final long serialVersionUID = 4950819240030644407L;
#JsonProperty(value = "cache_hit")
private Boolean cacheHit;
#JsonProperty(value = "created_on")
private Timestamp createdOn;
#JsonProperty(value = "fact_cache_error")
private String factCacheError;
#JsonProperty(value = "fact_cache_hit")
private Boolean factCacheHit;
#JsonProperty(value = "header_info")
private String headerInfo;
#JsonProperty(value = "json_request")
private String jsonRequest;
#JsonProperty(value = "name")
private String name;
#JsonProperty(value = "report_size")
private Integer reportSize;
#JsonProperty(value = "row_count")
private Integer rowCount;
#JsonProperty(value = "url")
private String url;
}
Now when I use the ObjectMapper to map to the ReportResponse object, I get the following error:
String jsonString = "{\"response\": {\"execution_status\": \"ready\", \"report\": {\"cache_hit\": true, \"created_on\": \"2013-07-29 09:53:44\", \"fact_cache_error\": null, \"fact_cache_hit\": false, \"header_info\": null, \"name\": null, \"report_size\": \"5871\", \"row_count\": \"33\", \"url\": \"report-download?id=2ff62c07fc3653b68f2073e7c1aa0517\"}, \"status\": \"OK\"}}";
ObjectMapper mapper = new ObjectMapper();
ReportResponse reportResponse = mapper.readValue(jsonString, ReportResponse.class);
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "report"
I know that I can create a separate Report class and then embed it in the ReportResponse with the #JsonProperty anotation. Is there a way I can avoid that and mark the ReportResponse class with an annotation which would map it to the "report" element in the JSON?
There is no annotation which could handle this case yet. There is a ticket requesting this feature.
Here is a brief statement from one of the owners regarding this topic.
Quote from the mentioned statement:
Tatu Saloranta: "… #JsonProperty does not support transformations, since the data binding is based on incremental parsing and does not have access to full tree representation. Supporting #JsonUnwrapped was non-trivial, but doable; and thus converse ("#JsonWrapped") would be doable, theoretically speaking. Just plenty of work. …"
I see couple of problems in your code. First thing is that you don't have report attribute in your Response class, which is required as per the json structure you have shown. Secondly you need to provide the getters and setters in your bean classes as those will be used by the jackson for marhsalling and unmarshalling of json/object.
Related
I'm doing a dummy app of a hostpital. The problem I'm having is that, I'm trying to verify that when a Patient is created, the fields passed are of the correct type, but whenever I POST an Int in a String field, it doesn't fail and just transform the Int to String. The field I'm trying to make fail is "surname", which by the definition of the Patient class, is a String.
If I do this (I pass a number to the "surname" field):
{
"name": "John",
"surname": 43,
"sickness": "headache"
}
It just transforms 43 into a String by the time its in the Controller method.
Here we have the Patient object:
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
public class Patient implements Serializable {
private static final long serialVersionUID = 4518011202924886996L;
#Id
//TODO: posible cambiar luego la generationType
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "patient_id")
private Long id;
#Column(name = "patient_name")
#JsonProperty(required = true)
private String name;
#Column(name = "patient_surname")
#JsonProperty(required = true)
private String surname;
#Column(name = "patient_sickness")
#JsonProperty(required = true)
private String sickness;
}
And this is the controller class:
#Controller
#Path("/patient")
#Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public class PatientController {
#POST
#Path("")
public ResponseEntity<Object> postPatient(final Patient patient) {
ResponseEntity<Object> createdPatient = patientBusiness.createPatient(patient);
return new ResponseEntity<Patient>(createdPatient.getBody(), createdPatient.getStatusCode());
}
EDIT 1:
Following the "clues" and closing the circle of attention, I tried modifying the ObjectMapper, but my configuration isn't applying. I'm still getting the error from above.
This is the config class:
#Configuration
public class JacksonConfig {
#Bean
#Primary
public ObjectMapper getModifiedObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, false);
mapper.coercionConfigFor(LogicalType.Integer).setCoercion(CoercionInputShape.String, CoercionAction.Fail);
return mapper;
}
}
Even added the property to the application.yml, but still nothing:
spring:
jackson:
mapper:
allow-coercion-of-scalars: false
Any help is appreciated. Thx.
In the end I referred to this post to do a deserializer and a module to just have it along all the program, not just the field I want not to be transformed.
Disable conversion of scalars to strings when deserializing with Jackson
I try to deserialize json :
{
"date": "2021_05",
"uuid": "3ba8b966-993f-49e0-b349-e528843a382c",
"dataset": "dataset",
"hmm_hit": "hit",
"hmm_evalue": "6.7e-186",
"hmm_score": "610.9"
},
I have two entities :
#Entity
public class HmmResult {
#Id
#GeneratedValue
#JsonIgnore
private Integer id;
#JsonProperty("hmm_hit")
private String hmm;
#JsonProperty("hmm_evalue")
private String eValue;
#JsonProperty("hmm_score")
private Float score;
}
and
#Entity
public class Protein {
#Id
#GeneratedValue
#JsonIgnore
private Integer id;
#JsonProperty("date")
private String date;
#JsonProperty("uuid")
private String uuid;
#JsonProperty("dataset")
private String dataset;
#OneToOne
#JsonDeserialize(as = HmmResult.class)
private HmmResult hmmResult;
How to deserialize both entities at the same time with one entry of json ?
Here is extract of main with Jackson ObjectMapper :
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Protein p = objectMapper.readValue(new File(file), Protein.class);
It parses "date", "uuid" and "dataset" fine but can not parse HmmResult object with "hmm_subfamily", "hmm_evalue" and "hmm_score" values : I get error : p.getHmmResult() : null. (HmmResult hm = objectMapper.readValue(new File(file), HmmResult.class); works fine too alone).
There is #JsonUnwrapped annotation for this. Should work like this:
public class Protein {
#JsonProperty("date")
private String date;
#JsonProperty("uuid")
private String uuid;
#JsonProperty("dataset")
private String dataset;
#JsonUnwrapped
private HmmResult hmmResult;
}
var protein = new ObjectMapper().readValue(new File(file), Protein.class);
I'm trying to serialize object to xml string with Jackson XmlMapper. My object is:
#JacksonXmlRootElement(namespace = "http://www.w3.org/2001/XMLSchema", localName = "PersonRO")
public class PersonInfo {
#JacksonXmlProperty(localName = "PersonID")
private String personId;
#JacksonXmlProperty(localName = "ReturnCode")
private Integer errorCode;
// getters, setters
}
I need to achieve following xml in output:
<PersonRO xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<PersonID>00000000000001</PersonID>
<ReturnCode>150</ReturnCode>
</PersonRO>
The task seems easy, but first of all I have a problem with achieving multiple namespases (xmlns:xsd, xmlns:xsi), and also have empty namespaces for fields, although I don't need them at all.
So far my result is:
<PersonRO xmlns="http://www.w3.org/2001/XMLSchema">
<PersonID xmlns="">00000000000001</PersonID>
<ReturnCode xmlns="">150</ReturnCode>
</PersonRO>
So, how can I achieve exactly the same result as above, using Jackson XmlMapper?
(I've seen that you can configure XmlFactory, etc., but can not do it properly...)
If you need any clarification, please let me know and thank you in advance.
I have found the answer:
#JacksonXmlRootElement(localName = "PersonRO")
public class PersonInfo {
#JacksonXmlProperty(isAttribute = true, localName = "xmlns:xsd")
private final String xmlnsXsd = "http://www.w3.org/2001/XMLSchema";
#JacksonXmlProperty(isAttribute = true, localName = "xmlns:xsi")
private final String xmlnsXsi = "http://www.w3.org/2001/XMLSchema-instance";
#JacksonXmlProperty(localName = "PersonID")
private String personId;
#JacksonXmlProperty(localName = "ReturnCode")
private Integer errorCode;
// getters, setters
}
I like to store a object like:
#Table(value = "my_table")
public class MyTableDto {
#PrimaryKeyColumn(name = "uid", type = PrimaryKeyType.PARTITIONED)
#CassandraType(type = DataType.Name.UUID)
private UUID uid;
#Column(value = "child_ids")
private List<ChildIdDto> childIds;
}
Then I get the exception:
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: Only primitive types are allowed inside Collections for property [childIds] of type ['interface java.util.List'] in entity [de.myapplication.repository.dto.MyTableDto]
I do understand the exception, but is there another way to persist custom objects?
EDIT:
When I comment out this attribute, everything works
! Never say never, I got the solution.
To give a good example, I will list all according classes.
ParentClass.java
#Table(value = "my_table") //OPT
public class MyTableDto {
#PrimaryKeyColumn(name = "uid", type = PrimaryKeyType.PARTITIONED)
#CassandraType(type = DataType.Name.UUID)
private UUID uid;
#Column(value = "child_ids") //OPT
private List<ChildDto> childIds;
}
ChildDto.java
#UserDefinedType // THE SOLUTION
public class ChildDto {
#Column(value = "child") //OPT
#CassandraType(type = DataType.Name.TEXT) //OPT
private String groupId;
#Column(value = "description") //OPT
#CassandraType(type = Name.TEXT) //OPT
private String description;
}
The #UserDefinedType is the solution.
For more information see here.
NOTE: Each annotation with "OPT" is NOT required
Here is my example:
Java:
#JsonProperty("id")
private String id;
#JsonProperty(value = "name", required = true)
private String deviceName;
I made the name as a required field. In request how to make it as required field. I should send the name value from request.
But when I enter this:
{ "id": "abc123",}
It should send error response back.
Please help me.
Jacksons JsonProperty Annotation is not used for Validation. see: Jackson #JsonProperty(required=true) doesn't throw an exception. But you can use Bean Validation, e.g.:
class Device {
#JsonProperty("id")
private String id;
#NotEmpty
#JsonProperty(value = "name")
private String deviceName;
}