ACCEPT_EMPTY_STRING_AS_NULL_OBJECT with List field - java

with Jackson, I'm trying to properly deserialize a JSON which contains empty string as no value values. Herein an example:
{
"code" : "REQ500",
"description" : "Problem with Service",
"params" : ""
}
and the bean I would get:
public final class MyError {
#JsonProperty("code")
private String code;
#JsonProperty("description")
private String description;
#JsonProperty("params")
private List<String> params;
// [CUT]
public void setParams(List<String> params) {
this.params = params;
}
}
Using ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, I've create an object mapper with
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true)
.registerModule(new SimpleModule().addDeserializer(LocalDate.class, new DateDeserializer()));
expecting to have params = null, but deserialization doesn't work. Herein the error I got:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot
deserialize instance of `java.util.ArrayList` out of VALUE_STRING token
at [Source: (String)"{
"code" : "REQ500",
"description" : "Problem with Service",
"params" : ""
}"; line: 4, column: 14] (through reference chain: solutions.infinitec.fabrick.models.common.MyError["params"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
Any hints on how to solve this issue? Am I wrong somewhere?
Thank you in advance for suggestions.

I tried it using ACCEPT_SINGLE_VALUE_AS_ARRAY and it works as expected.
ObjectMapper om = new ObjectMapper();
om.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
MyJson asString = om.readValue("{\n"
+ " \"code\" : \"REQ500\",\n"
+ " \"description\" : \"Problem with Service\",\n"
+ " \"params\" : \"\"\n"
+ "}", MyError.class);
MyJson asArray = om.readValue("{\n"
+ " \"code\" : \"REQ500\",\n"
+ " \"description\" : \"Problem with Service\",\n"
+ " \"params\" : [\"\"]\n"
+ "}", MyError.class);
The only thing you have to handle is the response containing just a single element in params which is empty.
Another way could be this definition of MyError:
public class MyError {
#JsonProperty("code")
private String code;
#JsonProperty("description")
private String description;
private List<String> params;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<String> getParams() {
return params;
}
#JsonProperty("params")
private void setParams(JsonNode params) {
if (params.isArray()) {
this.params = new ArrayList<>();
for(JsonNode child : params) {
this.params.add(child.asText());
}
}
}
}

I'd try using
OBJECT_MAPPER.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
As shown in "Cannot deserialize instance of java.util.ArrayList"

Just implement a setter method which accept string:
public void setParams(String params) {
this.params = null;
}
if want to accept array:
public void setParams(String[] params) {
this.params = Arrays.asList(params);
}

Related

How do I receive a dto, with a map as one of it's atributes, as a parameter of my controller? (REST API)

When I try to send it, it show me the "error": "Bad Request",
"trace": "...HttpMessageNotReadableException: JSON parse error: Cannot deserialize Map key of type java.time.LocalDate from String "quoteDate": Failed to deserialize java.time.LocalDate"
The JSON I am sending via postman:
{
"stockId":"test3",
"quotes":[
{
"quoteDate":"2003-05-14",
"quoteValue":"35.9"
},
{
"quoteDate":"2016-03-28",
"quoteValue":"55.0"
}
]
}
The controller:
#PostMapping("/stock/save")
public void saveQuotes(#RequestBody StockDTO stockDTO) {
System.out.println(stockDTO.toString());
}
The DTO
public class StockDTO {
String id;
String stockId;
Map<LocalDate, Double> quotes;
}
For quotes, the data type that should come as per the json string is a List. but was given a Map in the DTO.
The DTO should be ::
import com.google.gson.Gson;
import java.util.List;
public class GsonMappingExample {
public static void main(String[] args) {
String jsonString = "{\"stockId\":\"test3\",\"quotes\":[{\"quoteDate\":\"2003-05-14\",\"quoteValue\":\"35.9\"},{\"quoteDate\":\"2016-03-28\",\"quoteValue\":\"55.0\"}]}";
Gson gson = new Gson();
StockDTO stockDTO = gson.fromJson(jsonString, StockDTO.class);
System.out.println(stockDTO);
}
}
class StockDTO {
private final String stockId;
private final List<Quote> quotes;
public StockDTO(String stockId, List<Quote> quotes) {
this.stockId = stockId;
this.quotes = quotes;
}
#Override
public String toString() {
return "StockDTO{" +
"stockId='" + stockId + '\'' +
", quoteList=" + quotes +
'}';
}
}
class Quote {
private final String quoteDate;
private final Double quoteValue;
public Quote(String quoteDate, Double quoteValue) {
this.quoteDate = quoteDate;
this.quoteValue = quoteValue;
}
#Override
public String toString() {
return "Quote{" +
"quoteDate=" + quoteDate +
", quoteValue=" + quoteValue +
'}';
}
}
PS: Here I'm using Gson library to parse the json string, spring-boot automatically does that (using Jackson library I think!)

How to change the json response fields to user defined fields in java?

I have the following json response:
{
"Labels": {
"com.prop.vendor": "Acme",
"com.example.license": "GPL",
"com.example.version": "1.0"
}
}
Now I want to process these values in my java class. The problem I have is for fields like com.example.version, pojo is being generated using the same as data types. Like,
private String com.example.version
which gives me compile-time errors.
So, I want to create a field "String version" which points to "com.example.version"
And I have tried using #JsonProperty and #JsonSetter.
Labels.java
public class Labels
{
#JsonProperty("com.example.version")
private String version;
#JsonProperty("com.example.vendor")
private String vendor;
#JsonProperty("com.example.license")
private String license;
public String getVersion()
{
return version;
}
public void setVersion(String version)
{
this.version = version;
}
public String getVendor()
{
return vendor;
}
public void setVendor(String vendor)
{
this.vendor = vendor;
}
public String getLicense()
{
return license;
}
public void setLicense(String license)
{
this.license = license;
}
#Override
public String toString()
{
return "ClassPojo [com.example.version = " + version + ", com.example.vendor = " + vendor + ", com.example.license = "
+ license + "]";
}
}
But everytime I am getting this error:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "Labels" (class com.csc.agility.adapters.service.docker.pojo.Labels), not marked as ignorable (3 known properties: "com.example.license", "com.example.vendor", "com.example.version"])
at [Source: {
"Labels": {
"com.example.vendor": "Acme",
"com.example.license": "GPL",
"com.example.version": "1.0"
}
}; line: 2, column: 12] (through reference chain: com.csc.agility.adapters.service.docker.pojo.Labels["Labels"])
How I am using it:
ObjectMapper objMap = new ObjectMapper();
String jsonInString =
"{\n\"Labels\": {\n \"com.example.vendor\": \"Acme\",\n \"com.example.license\": \"GPL\",\n \"com.example.version\": \"1.0\"\n }\n}";
Label label = objMap.readValue(jsonInString, Label.class);
String licence = label.getLicense();
Can anyone point me to the correct json annotation to use or any other way to achieve it?
You should configure your mapper with
mapper.configure(Feature.UNWRAP_ROOT_VALUE, true); (for version 1.9)
or
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true); (for version 2 and above).
I have tried your json with above properties and everything works fine:
String data = "{\"Labels\": {\"com.example.vendor\": \"Acme\",\"com.example.license\": \"GPL\",\"com.example.version\": \"1.0\"}}";
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
Labels labels = mapper.readValue(data, Labels.class);
System.out.println(labels);
Note: In Json String, I have changed com.prop.vendor to com.example.vendor to make it workable.
Have one more pojo like below and use this object instead of Labels to serialize/deserialize -
public class Response {
#JsonProperty("Labels")
private Labels labels;
//setter
//getter
}
In your code use this -
Response response = objMap.readValue(jsonInString, Response.class);
String licence = response.getLabels().getLicense();

Ignore absent property when mapping json response

I have a form that should return list of customers.
This form should behave differently in two case:
User starts the research using only "surname"
User starts the research using surname AND name
In the first case the json response has less fields than the response in the second case so I have to ignore all these fields.
I've tried using #JsonInclude(JsonInclude.Include.NON_ABSENT), #JsonInclude(JsonInclude.Include.NON_EMPTY) and #JsonInclude(JsonInclude.Include.NON_NULL) but with each and everyone of these the error returned is always the same:
java.lang.Exception: Could not write content: (was java.lang.NullPointerException) (through reference chain: it.gruppoitas.itasacquire.pojo.Cliente["DATA_NASCITA"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: it.gruppoitas.itasacquire.pojo.Cliente["DATA_NASCITA"])
This is the pojo Cliente:
package it.gruppoitas.itasacquire.pojo;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
#JsonInclude(JsonInclude.Include.NON_ABSENT)
public class Cliente {
#JsonProperty("TIPO_PERSONA")
private String tipoPersona;
#JsonProperty("PRO_CLIE")
private String proClie;
#JsonProperty("CODICE_FISCALE")
private String codiceFiscale;
#JsonProperty("DATA_NASCITA")
private String dataNascita;
#JsonProperty("SESSO")
private String sesso;
#JsonProperty("NOME")
private String nome;
#JsonProperty("COGNOME")
private String cognome;
public String getTipoPersona() {
return tipoPersona;
}
public void setTipoPersona(String tipoPersona) {
this.tipoPersona = tipoPersona;
}
public String getProClie() {
return proClie;
}
public void setProClie(String proClie) {
this.proClie = proClie;
}
public String getCodiceFiscale() {
return codiceFiscale;
}
public void setCodiceFiscale(String codiceFiscale) {
this.codiceFiscale = codiceFiscale;
}
public String getDataNascita() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S");
Date data = null;
try {
data = sdf.parse(dataNascita);
dataNascita = new SimpleDateFormat("dd/MM/yyyy").format(data);
} catch (ParseException e) {
System.err.println(e);
}
return dataNascita;
}
public void setDataNascita(String dataNascita) {
this.dataNascita = dataNascita;
}
public String getSesso() {
return sesso;
}
public void setSesso(String sesso) {
this.sesso = sesso;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getCognome() {
return cognome;
}
public void setCognome(String cognome) {
this.cognome = cognome;
}
#Override
public String toString() {
return "Cliente [tipoPersona=" + tipoPersona + ", proClie=" + proClie + ", codiceFiscale=" + codiceFiscale + ", dataNascita="
+ dataNascita + ", sesso=" + sesso + ", nome=" + nome + ", cognome=" + cognome + "]";
}}
Any idea?
EDIT: this is an example of the json response structure in case 1
{
"TIPO_PERSONA" : "G",
"PRO_CLIE" : "123456789",
"CODICE_FISCALE" : "123456789",
"PARTITA_IVA" : "123456789",
"SESSO" : "S",
"COGNOME" : "CUSTOMER SRL"
}
And this is an example of the json response in case 2:
{
"TIPO_PERSONA" : "F",
"PRO_CLIE" : "123456789",
"CODICE_FISCALE" : "123456789",
"DATA_NASCITA" : "1969-09-07 00:00:00.0",
"SESSO" : "F",
"NOME" : "Foo",
"COGNOME" : "Fie"
}
As you can see there are less fields in case 1 and STS goes in full-panic mode...
You need to configure your object mapper not to fail on empty beans.
Here is a sample code since you didn't provide the creation of the ObjectMapper code yourself:
private ObjectMapper jacksonMapper = new ObjectMapper();
jacksonMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
jacksonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
You can also use:
jacksonMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES,false);

JSON: JsonMappingException while try to deserialize object with null values

I try to deserialize object that contains null-properties and have the JsonMappingException.
What I do:
String actual = "{\"#class\" : \"PersonResponse\"," +
" \"id\" : \"PersonResponse\"," +
" \"result\" : \"Ok\"," +
" \"message\" : \"Send new person object to the client\"," +
" \"person\" : {" +
" \"id\" : 51," +
" \"firstName\" : null}}";
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(new StringReader(json), PersonResponse.class); //EXCEPTION!
BUT: if to throw away "firstName = null" property - all works fine!
I mean pass the next string:
String test = "{\"#class\" : \"PersonResponse\"," +
" \"id\" : \"PersonResponse\"," +
" \"result\" : \"Ok\"," +
" \"message\" : \"Send new person object to the client\"," +
" \"person\" : {" +
" \"id\" : 51}}";
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(new StringReader(json), PersonResponse.class); //ALL WORKS FINE!
Question:
How to avoid this exception or to pledge Jackson ignore null-values during serialization?
Throws:
Message:
com.fasterxml.jackson.databind.MessageJsonException:
com.fasterxml.jackson.databind.JsonMappingException:
N/A (through reference chain: person.Create["person"]->Person["firstName"])
cause:
com.fasterxml.jackson.databind.MessageJsonException:
com.fasterxml.jackson.databind.JsonMappingException:
N/A (through reference chain: prson.Create["person"]->Person["firstName"])
cause: java.lang.NullPointerException
Sometimes this problem occurs when accidentally using a primitive type as return type of the getter of a non-primitive field:
public class Item
{
private Float value;
public float getValue()
{
return value;
}
public void setValue(Float value)
{
this.value = value;
}
}
Notice the "float" instead of "Float" for the getValue()-method, this can lead to a Null Pointer Exception, even when you have added
objectMapper.setSerializationInclusion(Include.NON_NULL);
If you don't want to serialize null values, you can use the following setting (during serialization):
objectMapper.setSerializationInclusion(Include.NON_NULL);
Hope this solves your problem.
But the NullPointerException you get during deserialization seems suspicious to me (Jackson should ideally be able to handle null values in the serialized output). Could you post the code corresponding to the PersonResponse class?
I also faced the same issue.
I just included a default constructor in the model class along with the other constructor with parameters.
It worked.
package objmodel;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
public class CarModel {
private String company;
private String model;
private String color;
private String power;
public CarModel() {
}
public CarModel(String company, String model, String color, String power) {
this.company = company;
this.model = model;
this.color = color;
this.power = power;
}
#JsonDeserialize
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
#JsonDeserialize
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
#JsonDeserialize
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
#JsonDeserialize
public String getPower() {
return power;
}
public void setPower(String power) {
this.power = power;
}
}
Add JsonProperty annotation to your attribute in TO class, as below
#JsonProperty
private String id;

Deserialize JSON string into String Array

How to get this string and put into String Array or HashMap? Any reference?
{
"code" : 403,
"errors" : [ {
"domain" : "global",
"location" : "Authorization",
"locationType" : "header",
"message" : "The domain policy has disabled third-party Drive apps",
"reason" : "domainPolicy"
} ],
"message" : "The domain policy has disabled third-party Drive apps"
}
====
This is my solution for Jackson:
public class TheErrorModel {
public int code;
public Collection<Error> errors;
public String message;
public TheErrorModel() {
}
public TheErrorModel(String json)
throws Exception {
ObjectMapper mapper = new ObjectMapper();
TheErrorModelother = mapper.readValue(json, TheErrorModel.class);
this.code = other.code;
this.errors = other.errors;
this.message = other.message;
}
}
class Error {
public String domain;
public String location;
public String locationType;
public String message;
public String reason;
}
TheErrorModel theErrorModel = new TheErrorModel(json);
So I get something like theErrorModel.message :D
You can try to use GSON http://code.google.com/p/google-gson/ , It is pretty simple to use. You should just create class structure for this json schema.
import com.google.gson.Gson;
class YourErrorsJSON{
private String domain
private String location;
private String locationType;
private String message;
private String reason
}
class YourJSON {
private int code;
private YourErrorsJSON[] errors;
private String message;
}
Gson gson = new Gson();
YourJSON obj = gson.fromJson(json, YourJSON.class);
Try this ::
JSONObject yourObj = JSONObject.fromString(input_string);
String code = yourObj.get("code"); // code will have a value 403

Categories

Resources