Deserialize JSON to string in raw format using Jackson - json

I have a use case where I want the JSON to be converted to string as it is, but it is failing and giving me null, here is my POJO:
#Data
#JsonSnakeCase
#JsonIgnoreProperties(ignoreUnknown = true)
public class DocumentTemplateRequest {
#Enumerated(EnumType.STRING)
private TemplateState state;
#JsonDeserialize(using = JsonAsStringDeserializer.class)
private String inputSchema;
}
the Json I am using as payload:
{
"state": "staging",
"input_schema": {
"title": "Person",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
}
},
"required": ["firstName", "lastName"]
}
}
I am using object mapper for mappings:
ObjectMapper objectMapper = new ObjectMapper();
DocumentTemplateRequest documentTemplateRequest = null;
try {
documentTemplateRequest = objectMapper.readValue(str, DocumentTemplateRequest.class);
} catch (IOException e) {
e.printStackTrace();
}
and here is my deserializer:
public class JsonAsStringDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
TreeNode tree = jsonParser.getCodec().readTree(jsonParser);
return tree.toString();
}
}
and the results of deserialization is :
state : staging
inputSchema: null
why inputSchema is coming as null, what am I missing?

it is with the property?
you are missing underscroe. input_schema is different from inputSchema.
either use the same property name everywhere you use it or use jsonproperty annotation to be sepecific.

Related

I Cannot deserialize instance of `java.lang.String` out of START_OBJECT token

i am trying map a json file to json object but i am getting an error as cannot deserialize string out of start_object token.
This is my json file
{
"serial_key": 2,
"serial_identity" : "20200580011000",
"serial_type" : "I",
"serial_source" : "NASA"
"serial_detail" : {
"serialID": "20200580011000",
"patient": {
"name": {
"fullName": "John Mathew",
"lastName": "John",
"firstName": "Mathew"
},
"gender": "Female",
"century": "0",
"dateOfBirth": 252460800000,
"admissionStatus": "active"
}
}
}
Below is my mapping class
#Data
#NoArgsConstructor
#AllArgsConstructor
public class SerialDto {
#JsonProperty("serial_key")
private Long serialKey;
#JsonProperty("serial_identity")
private String serialIdentity;
#JsonProperty("serial_type")
private String serialType;
#JsonProperty("serial_source")
private String serialSource;
#JsonProperty("serial_detail")
private String serialDetail;
}
Below is the code to map it
String json;
ObjectMapper mapper = new ObjectMapper();
try {
json = FileUtils.readFileToString(new File("src/main/resources/test.json"), StandardCharsets.UTF_8);
SerialDto dto = mapper.readValue(json, SerialDto.class);
System.out.println(dto.getSerialIdentity());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
The exception which i am getting is
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
(through reference chain: com.app.entity.SerialDto["serial_detail"])
can anyone help to bind the serial_detail to the string of serialDetail where is there the json format need to be modified in order to get the serial_detail as json string?

How to omit null field from Swagger/OpenAPI in ResponseEntity?

I'm trying to omit null values in my ResponseEntity.
My controller looks something like this:
#RestController
public class FooController {
//fields
//constructor
#PostMapping
public ResponseEntity<CreateFooResponseV10> createFoo(#Valid #RequestBody CreateFooRequestV10 foo, HttpServletRequest request) {
//some minor logic
return new ResponseEntity<>(aFooResponseV10Builder()
.withFirstName(foo.getFirstName())
.withLastName(foo.getLastName())
.withTestField(NULLABLE_OBJECT)
.build(), ...);
//I generated the builders from the output classes openapi-generator provided
}
// more stuff...
}
When NULLABLE_OBJECT is equal to null I expect the field to be omitted from the response like this:
{
"firstName": "John",
"lastName": "Doe"
}
But I either get these responses, depending on what I've tried so far:
{
"firstName": "John",
"lastName": "Doe",
"testField": null
}
or
{
"firstName": "John",
"lastName": "Doe",
"testField": {"present":false}
}
I generate my request/response objects (CreateFooResponseV10 and CreateFooRequestV10) with the use of openapi-generator
Here is my redacted api.json file:
{
"openapi": "3.0.1",
"info": { ... },
"servers": [ ... ],
"paths": {
"/foo": {
"post": {
...
"requestBody": {
"description": "Foo to be created",
"content": {
"application/foo+json;version=1.0": {
"schema": {
"$ref": "#/components/schemas/CreateFooRequest_V1_0"
}
}
},
"required": true
},
"responses": {
"201": {
"description": "Foo is successfully created",
"headers": { ... },
"content": {
"application/foo+json": {
"schema": {
"$ref": "#/components/schemas/CreateFooResponse_V1_0"
}
}
}
},
...
}
}
}
},
"components": {
"schemas": {
"CreateFooRequest_V1_0": {
"required": [
"firstName",
"lastName"
],
"type": "object",
"properties": {
"firstName": { ... },
"lastName": { ... },
"testField": {
"description": "...",
"type": "string",
"nullable": true
}
}
},
"CreateFooResponse_V1_0": {
"required": [
"firstName",
"lastName"
],
"type": "object",
"properties": {
"firstName": { ... },
"lastName": { ... },
"testField": {
"description": "...",
"type": "string",
"nullable": true
}
}
}
}
}
}
As you can see in both the request and response testField is not required and can be nullable.
So when testField is null it should be hidden from the response, but when it contains some date it should be shown of course.
I've tried overriding jackson's ObjectMapper bean as explained in this answer. Didn't work.
I've tried adding spring.jackson.default-property-inclusion=non_null to the application.properties. Didn't work.
What I think should work is adding #JsonIgnore above testField of the generated classes, but I don't know if this is something needed to be done manually (for each schema component, can be a lot of manual work for something that is generated) or if this can be configured in the plugin somewhere.
Thanks in advance.
extra info
OpenAPI 3.0.1
Maven 3.6.3
Java 11.0.2
jackson-databind-nullable 0.2.1
openapi-generator-maven-plugin 4.2.2
You can set the following in application.properties
spring.jackson.default-property-inclusion = NON_NULL
See Customize the Jackson ObjectMapper
Note: To make use of this, you need to #Autowire the ObjectMapper, and not manually create it
Try registering the following bean in your spring context. It should override default bean
#Bean
public HttpMessageConverters httpMessageConverters() {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL)
return new HttpMessageConverters(
new MappingJackson2HttpMessageConverter(mapper));
}
You can generate the model classes with additional class annotations using OpenApi generator.
Just need to include this in your maven plugin:
<configOptions>
<additionalModelTypeAnnotations>
#com.fasterxml.jackson.annotation.JsonInclude(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL)
<additionalModelTypeAnnotations>
<configOptions>
see other config options here:
https://openapi-generator.tech/docs/generators/spring/
Try this code. I tested and it works.
#RestController
#RequestMapping("/testws")
public class TestWS {
#RequestMapping(value = "test", method = { RequestMethod.POST,
RequestMethod.GET }, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<TestBean> test(#Context HttpServletRequest request) {
TestBean testBean = new TestBean("John", "Doe", null);
return ResponseEntity.ok()
.body(testBean);
}
}
#JsonInclude(Include.NON_NULL)
class TestBean {
private String firstName;
private String lastName;
private String testField;
public TestBean() {
}
public TestBean(String firstName, String lastName, String testField) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.testField = testField;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getTestField() {
return testField;
}
public void setTestField(String testField) {
this.testField = testField;
}
}
Json response:
{"firstName":"John","lastName":"Doe"}

Why am I getting Jackson ObjectMapper Parsing Error due to some issues in LinkedHashMap initiation?

I want to read this JSON using ObjectMapper:
{
"basePath": "/v1",
"models": {
"Course":{
"number": "integer",
"name": "string",
"description": "string"
},
"Department": {
"name": "string",
"code": "string"
}
}
}
I used Jackson ObjectMapper like this:
ObjectMapper mapper = new ObjectMapper();
InputStream inputStream = Input.class.getResourceAsStream("/input.json");
Input input = mapper.readValue(inputStream, Input.class);
Where Input is:
public class Input {
String basePath;
Map<String, Map<String, Map<String, String>>> models;
public String getBasePath() {
return basePath;
}
public void setBasePath(String basePath) {
this.basePath = basePath;
}
public Map<String, Map<String, Map<String, String>>> getModels() {
return models;
}
public void setModels(Map<String, Map<String, Map<String, String>>> models) {
this.models = models;
}
}
But I am getting JSON Mapping Error.
Can not instantiate value of type [map type; class java.util.LinkedHashMap, [simple type, class java.lang.String] -> [simple type, class java.lang.String]] from JSON String; no single-String constructor/factory method
What am I doing wrong here?
The provide json structure does not matches with the models map structure. You have two options for it either change in Map or change in json.
Map<String, Map<String, String>> models;
If change in json structure like below.
{
"basePath": "/v1",
"models": {
"Course":{
"Details":{
"number": "integer",
"name": "string",
"description": "string"
}
}
}
}
I think better to change in Map thing rather to change the JSON structure.

deserialization of a JSON API response with moshi

I got a null object attributes after deserialization of a json response.
Developing under android, I'm using retrofit2 , moshi as converter (https://github.com/kamikat/moshi-jsonapi ) .
When debugging ,I saw a json response fully retrieved (not null attributes),but deserialization fails. Should I use GSON instead?
Here's my retrofit builder I use to make my json call: (no issue)
public static JsonServerInterface getSimpleClient(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_AUTH_URL)a
.addConverterFactory(MoshiConverterFactory.create())
.build();
JsonServerInterface webServer=retrofit.create(JsonServerInterface.class);
return webServer;
}
My api json call,response contain UserModel with null attributes(deserialization fails without any error)
signInCall.enqueue(new Callback<UserModel>(){
#Override
public void onResponse
(Call<UserModel> call, Response<UserModel> response)
{
response.message();
}
}
My UserModel (as required by moshi ,but I think it lacks something):
#JsonApi(type = "users")
public class UserModel extends Resource {
#Json(name = "auth-token")
private String authToken;
#Json(name = "firstname")
private String firstname;
#Json(name = "lastname")
private String lastname;
#Json(name = "email")
private String email;
#Json(name = "created-at")
private String createdAt;
#Json(name = "updated-at")
private String updatedAt;
private HasMany<ActivityModel> activities;
My json response I saw when debugging http response, I retrieve without any trouve,but moshi sucks to deserialize it,and no errors are raised:
{
"data": {
"id": "21",
"type": "users",
"attributes": {
"auth-token": "t8S3BTqyPwN3T4QDMY1FwEMF",
"firstname": "aymen",
"lastname": "myself",
"email": "aymen.myself#gmail.com",
"created-at": "2017-11-13T22:52:39.477Z",
"updated-at": "2017-11-13T23:21:09.706Z"
},
"relationships": {
"activities": {
"data": [
{
"id": "81",
"type": "activities"
}
]
}
}
},
"included": [
{
"id": "81",
"type": "activities",
"attributes": {
"title": "activity 10",
"description": "how to draw a circle",
"start-at": "2017-11-13T23:06:13.474Z",
"duration": 10,
"created-at": "2017-11-13T23:06:32.630Z",
"updated-at": "2017-11-13T23:06:32.630Z"
},
"relationships": {
"user": {
"data": {
"id": "21",
"type": "users"
}
}
}
}
]
}
I find the solution after lot of hours:
I should use "Document" instead of UserModel
interface:
#POST("sign-in.json")
Call<Document> signIn(#Body Credentials credentials);
when calling:
signInCall.enqueue(new Callback<Document>(){
#Override
public void onResponse(Call<Document> call, Response<Document> response) {
hope it helps

Not able to Parse JSON data to POJO

I have JSON data with following structure. I have tried creating POJOs with same structure and with same names. I have taken a DTO which contains a list (of DTOs with structure of the numeral objects in the following JSON) and a String "Notice". I am not able to get the data in the DTO.
{
"notice": "This API is in a pre-launch state, and will go through significant changes.",
"1": {
"next_renewal_date": "2014-08-01",
"next_renewal_fee": {
"price": "800.0",
"currency": "USD"
},
"next_renewal_description": "1st Annuity - Official Fee",
"next_per_claim_fee": {
"price": "0.0",
"currency": "USD",
"free_claims": 0,
"claim_type": "claims_count"
},
"next_agent_fee": {
"price": "0.0",
"currency": "USD"
},
"grace_period_end_date": "2015-02-01"
},
"2": {
"next_renewal_date": "2018-08-01",
"next_renewal_fee": {
"price": "1800.0",
"currency": "USD"
},
"next_renewal_description": "2nd Annuity - Official Fee",
"next_per_claim_fee": {
"price": "0.0",
"currency": "USD",
"free_claims": 0,
"claim_type": "claims_count"
},
"next_agent_fee": {
"price": "0.0",
"currency": "USD"
},
"grace_period_end_date": "2019-02-01"
}
}
POJO:
public class RenewalAPICallListDTO {
private Map<Integer,JSONCallDto> apiCallList;
public Map<Integer, JSONCallDto> getApiCallList() {
return apiCallList;
}
public void setApiCallList(Map<Integer, JSONCallDto> apiCallList) {
this.apiCallList = apiCallList;
}
private String notice;
public String getNotice() {
return notice;
}
public void setNotice(String notice) {
this.notice = notice;
}
}
Method call:
Gson gson = new Gson();
RenewalAPICallListDTO respDto = gson.fromJson(response1.toString(), RenewalAPICallListDTO.class);
What you are looking for can be achieve with Jackson with a custom deserializer, as the following:
public class CustomDeserializer extends JsonDeserializer<RenewalAPICallListDTO> {
private static final ObjectMapper mapper = new ObjectMapper();
static {
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
#Override
public RenewalAPICallListDTO deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException {
JsonNode node = jp.getCodec().readTree(jp);
Iterator<Entry<String, JsonNode>> nodeIterator = node.fields();
RenewalAPICallListDTO dto = new RenewalAPICallListDTO();
Map<Integer, JsonCallDto> map = new HashMap<>();
while (nodeIterator.hasNext()) {
Map.Entry<String, JsonNode> entry = nodeIterator.next();
if (entry.getKey().equalsIgnoreCase("notice")) {
dto.setNotice(entry.getValue().toString());
} else {
map.put(Integer.parseInt(entry.getKey()), mapper.readValue(entry.getValue().toString(), JsonCallDto.class));
}
}
dto.setApiCallList(map);
return dto;
}
}
Usage:
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(RenewalAPICallListDTO.class, new CustomDeserializer());
mapper.registerModule(module);
RenewalAPICallListDTO dto = mapper.readValue(JSON, RenewalAPICallListDTO.class);
}
The final dto will be correctly serialized like you want, even with the correct types already set.
The json and POJO do not match. The attribute apiCallList is missing in your JSON string.
The structure should be like:
{
"notice": "random string",
"apiCallList": {
"1": {
"next_renewal_date": "2014-08-01",
...
},
"2": {
"next_renewal_date": "2014-08-01",
....
}
}
}
I have found a way. Thanks for the help.
I have converted the JSON into Hashmap using :
Map data = mapper.readValue(json,Map.class);
and then iterated the map, using the objects to populate POJOs.