Ignore absent property when mapping json response - java

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);

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!)

Java Spring, unmarshal dynamic json

I am trying to unmarshal a json response which comes from a server. But I would like to know which is the best way, approach to use when the json response changes.
For example, if I have a json response like this:
{
"name": "John",
"last_name": "John Last name",
"date_of_birth": "01.01.1990"
}
With jackson, I could deserialize the json object into a Person.class like this:
#NoARgsConstructor
#Setter
public class Person(){
private String name;
private String lastName;
private String dateOfBirth;
}
But what if the json struct changes, and now the attributes of the person comes inside a person object.
{
"person": {
"name": "John",
"last_name": "John Last name",
"date_of_birth": "01.01.1990"
}
}
What would be the best way to avoid this things or to avoid this problems? Is there any possible solution or approach to implement in java spring?
How about searching in the JSON yourself?
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class Foo {
public static void main(String args[]) throws Exception {
String jsonString = "{\"foo\":\"bar\"}";
ObjectMapper mapper = new ObjectMapper();
ObjectNode node = mapper.readValue(jsonString, ObjectNode.class);
if(node.has("foo")) {
System.out.println("foo: " + node.get("foo"));
}
}
}
To make it completely dynamic, I have used reflection api and json-simple-1.1 jar and jackson
import com.fasterxml.jackson.annotation.JsonProperty;
public class Person {
#JsonProperty("name")
private String name;
#JsonProperty("last_name")
private String lastName;
#JsonProperty("date_of_birth")
private String dateOfBirth;
}
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Demo {
public static void main(String a[]) {
try {
String json1 = "{\r\n" + " \"person\": {\r\n" + " \"name\": \"John\",\r\n"
+ " \"last_name\": \"John Last name\",\r\n" + " \"date_of_birth\": \"01.01.1990\"\r\n"
+ " } \r\n" + " }";
String json2 = "{\r\n" + " \"name\": \"John\",\r\n" + " \"last_name\": \"John Last name\",\r\n"
+ " \"date_of_birth\": \"01.01.1990\"\r\n" + "} ";
extractedPersonObject(json1);
System.out.println("*****************************");
extractedPersonObject(json2);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void extractedPersonObject(String json2) throws ParseException {
Person person = new Person();
Object obj = new JSONParser().parse(json2);
JSONObject jo = (JSONObject) obj;
Map finalMap;
Field[] fields = Person.class.getDeclaredFields();
if (jo.get(fields[0].getName()) == null) {
finalMap = ((Map) jo.get("person"));
} else
finalMap = (Map) jo;
for (Field field : fields) {
JsonProperty jsonProperty = field.getDeclaredAnnotation(JsonProperty.class);
invokeSetter(person, field.getName(), finalMap.get(jsonProperty.value()));
}
System.out.println(person.getDateOfBirth());
System.out.println(person.getLastName());
System.out.println(person.getName());
}
public static void invokeSetter(Object obj, String propertyName, Object variableValue) {
PropertyDescriptor pd;
try {
pd = new PropertyDescriptor(propertyName, obj.getClass());
Method setter = pd.getWriteMethod();
try {
setter.invoke(obj, variableValue);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
}
A generic way to deserialize such wrapped is to write a deserializer of your own, like:
#SuppressWarnings("serial")
public class UnwrappingDeserializer<T> extends StdDeserializer<T> {
#Setter
private String fieldName;
private ObjectMapper innerMapper = new ObjectMapper();
public UnwrappingDeserializer(Class<T> vc) {
super(vc);
fieldName = handledType().getSimpleName().toLowerCase();
}
#SuppressWarnings("unchecked")
#Override
public T deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode rootT = p.readValueAsTree();
// Check if there is a node with specified field name.
// There is also a setter for it if the field name is not
// directly resolvable
JsonNode valueT = rootT.get(fieldName);
if (valueT == null) {
// If no such node it is the root tha has the value
valueT = rootT;
}
return innerMapper.convertValue(valueT, (Class<T>)handledType());
}
}
Assuming you have Person as:
#Getter #Setter
// Below is because of your JSON key format
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class Person {
private String name;
private String lastName;
private String dateOfBirth;
}
you can just add the deserializer to your ObjectMapper like:
ObjectMapper om = new ObjectMapper();
SimpleModule sm = new SimpleModule();
sm.addDeserializer(Person.class, new UnwrappingDeserializer<Person>(Person.class));
om.registerModule(sm);

Jackson: Ignore deserializing one property and leave it as list of strings [duplicate]

I've got a following JSON from API:
"hotel_data": {
"name": "Hotel Name",
"checkin_checkout_times": {
"checkin_from": "14:00",
"checkin_to": "00:00",
"checkout_from": "",
"checkout_to": "12:00"
},
"default_language": "en",
"country": "us",
"currency": "USD",
"city": "Miami"
}
I'm using Jackson library to deserialize this JSON to Java object. I don't want to create a special class for checkin_checkout_times object. I just want to get it as a plain text. Like this "checkin_from": "14:00", "checkin_to": "00:00", "checkout_from": "", "checkout_to": "12:00".
In my POJO for hotel_data this checkin_checkout_times should be as a string i.e.:
#JsonProperty("checkin_checkout_times")
private String checkinCheckoutTimes
Is this possible to get this part of the JSON as a plain text?
EDIT: Error that I'm getting com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of java.lang.String out of START_OBJECT token
at [Source: (String)...
Make use of JsonNode.
Just make the following setter for the field checkinCheckoutTimes in your POJO for hotel_data and it should work for you.
public void setCheckinCheckoutTimes(JsonNode node) {
this.checkinCheckoutTimes = node.toString();
}
Example
String str = "{ \"id\": 1, \"data\": { \"a\": 1 } }";
try {
System.out.println(new ObjectMapper().readValue(str,Employee.class));
} catch (IOException e) {
e.printStackTrace();
}
Where Employee is as follows:
class Employee
{
private int id;
private String data;
public Employee() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getData() {
return data;
}
public void setData(JsonNode node) {
this.data = node.toString();
}
#Override
public String toString() {
return "Employee{" +
"id=" + id +
", data='" + data + '\'' +
'}';
}
}
gives the following output:
Employee{id=1, data='{"a":1}'}
You can also write a custom deserializer as described in the article:
public class RawJsonDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode node = mapper.readTree(jp);
return mapper.writeValueAsString(node);
}
}
and then use it with annotation in your class:
public class HotelData {
#JsonProperty("checkin_checkout_times")
#JsonDeserialize(using = RawJsonDeserializer.class)
private String checkinCheckoutTimes;
// other attributes
// getters and setters
}

how to create Java getters and setters when object key is number

I cannot create Java Getters and Setters, because I got number(digit) for my Object Key.
I will show you my API response. How can I achieve this without changing the API.
{"api_status": true,
"message": "",
"data": {
"0": {
"id": "aaa",
"name": "aaa",
"address": "aaa",
"category": "aaa",
"open_24_hours": "aaa",
"business_open": "",
"business_close": "",
"type": "0",
"title": null,
"latitude": "6.8729428",
"longitude": "79.8689013",
"city": "",
"distance": "2.95555089735992"
},
"1": {
"id": "bbb",
"name": "bbb",
"address": "bbb",
"category": "bbb",
"open_24_hours": "bbb",
"business_open": "",
"business_close": "",
"type": "0",
"title": null,
"latitude": "6.8767581",
"longitude": "79.8674747",
"city": "",
"distance": "2.915385898910569"
},
}
}
Use the below class and pass it to GSON library with your json data and the Class As a model . you will get your model, each data item is mapped with hashtable where key is your number which i represent as string By iterating over hash map you will get keySet which is your all keys in the data key of json. and for each key you can get itemData.
class JsonStructure{
public boolean api_status;
public String message
HashMap<String,ItemsData> data;
}
class ItemsData{
public String id;
public String name;
public String address;
public String category;
public String open_24_hours;
public String business_open;
public String business_close;
public String type;
public String title;
public String latitude;
public String longitude;
public String city;
public String distance;
}
For retrofit Build
BuildRetrofit(){
mOkHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
mConverterFactory = GsonConverterFactory.create();
String baseUrl = "http://dev.appslanka.com/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(mOkHttpClient)
.addConverterFactory(mConverterFactory)
.build();
mApi = retrofit.create(ApiInterface.class);
}
In ApiInterface define yoyr request method
interface ApiInterface{
#GET("_test/placeInDistance/")
Call<JsonStructure> getResponseForApiCall();
}
Now call this method as retrofit call structure:
Call<JsonStructure> call = mApi.getResponseForApiCall();
Response<JsonStructure> response = call.execute();
Parse this response like below:
HashMap<String, ItemsData> map = response .data;
Set<String> s = map.keySet();
Iterator<String> i = s.iterator();
while (i.hasNext()){
String key = i.next();
ItemsData data = map.get(key);
String id = data.id;
String name = data.name;
String address = data.address;
String category = data.category;
String open24Hr = data.open_24_hours;
String businessOpen = data.business_open;
String close = data.business_close;
String latitue = data.latitude;
..... etc
}
Yes, you can. Use SerializedName annotation like this:
#SerializedName("0")
private MyClass myObject;
Where MyClass is gonna represent a POJO for the data you're getting back.
I just want to note that a better solution would be to change the API (cause this response is weird), to return a list rather than an object with digits for keys, but I can see that you wrote in the question that you cannot change it.
If you really need to parse this JSON. Use custom solution.
For example my solution.
Create class Response with following code :
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
public class Response {
public boolean apiStatus;
public String message;
public List<Data> datas;
public Response(JSONObject jsonObject) {
apiStatus = jsonObject.optBoolean("api_status");
message = jsonObject.optString("message");
datas = new ArrayList<>();
try {
JSONObject datasJSON = jsonObject.getJSONObject("data");
int index = 0;
while (datasJSON.has(String.valueOf(index))) {
JSONObject dataJSON = datasJSON.getJSONObject(String.valueOf(index));
datas.add(new Data(dataJSON));
index++;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override public String toString() {
return "Response{" +
"apiStatus=" + apiStatus +
", message='" + message + '\'' +
", datas=" + datas +
'}';
}
}
Create class Data with following code :
import org.json.JSONObject;
public class Data {
public String id;
public String name;
public String address;
public String category;
public String open24Hours;
public String businessOpen;
public String businessClose;
public String type;
public String title;
public String latitude;
public String longitude;
public String city;
public String distance;
public Data(JSONObject jsonObject) {
id = jsonObject.optString("id");
name = jsonObject.optString("name");
address = jsonObject.optString("address");
category = jsonObject.optString("category");
open24Hours = jsonObject.optString("open_24_hours");
businessOpen = jsonObject.optString("business_open");
businessClose = jsonObject.optString("business_close");
type = jsonObject.optString("type");
title = jsonObject.optString("title");
latitude = jsonObject.optString("latitude");
longitude = jsonObject.optString("longitude");
city = jsonObject.optString("city");
distance = jsonObject.optString("distance");
}
#Override public String toString() {
return "Data{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", address='" + address + '\'' +
", category='" + category + '\'' +
", open24Hours='" + open24Hours + '\'' +
", businessOpen='" + businessOpen + '\'' +
", businessClose='" + businessClose + '\'' +
", type='" + type + '\'' +
", title='" + title + '\'' +
", latitude='" + latitude + '\'' +
", longitude='" + longitude + '\'' +
", city='" + city + '\'' +
", distance='" + distance + '\'' +
'}';
}
}
Instruction for use this solution:
Response response = new Response(jsonObject);
Instruction for use it, when you use Retrofit2.
For first we need to create custom factory, create class with name ResponseRetrofitConverter, and this following code :
import android.support.annotation.NonNull;
import org.json.JSONObject;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
public class ResponseRetrofitConverter extends Converter.Factory {
public static ResponseRetrofitConverter create() {
return new ResponseRetrofitConverter();
}
#Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return new JsonConverter();
}
private final static class JsonConverter implements Converter<ResponseBody, Response> {
#Override
public Response convert(#NonNull ResponseBody responseBody) {
try {
return new Response(new JSONObject(responseBody.string()));
} catch (Exception e) {
return null;
}
}
}
}
When Response is your entity,
Add connect with factory to retrofit use following code line :
.addConverterFactory(ResponseRetrofitConverter.create())
For example my code:
Retrofit.Builder()
.baseUrl(link)
.addConverterFactory(ResponseRetrofitConverter.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
You should create a java List of objects to represent the data.
If you want to bind a Json that has a number as name, and if you are using jackson as json library, you can declare the variable as follow:
#JsonProperty("0")
private CustomObject zero;
#JsonProperty("1")
private CustomObject one;
public CustomObject getZero()
{
return this.zero;
}
public void setZero(CustomObject zero)
{
this.zero= zero;
}
public CustomObject getOne()
{
return this.one;
}
public void setOne(CustomObject one)
{
this.one= one;
}
If you are using Gson then you can use as follows:
public class Model{
#SerializedName("0")
private String object;
}
You can call you class _0, _1... even it's a little bit strange.

validate json payload in spring boot rest to throw error if unknown property or empty property has been sent as part of json payload

JSON Request:
{
"notificationType" : "ISSUER_OTP1ee2asasa",
"content" : "hi fff this is fff template content for SBI email good and mobile dfdfdfd and remaining balance is 333 and your name is hahaha.",
"medium" : "EMAIL",
"asa":"ddddd",
"":""
}
POJO:
package com.innoviti.notification.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
#Document(collection = "NotificationTemplate")
#JsonIgnoreProperties(ignoreUnknown=false)
public class NotificationTemplate {
#JsonCreator
public NotificationTemplate(#JsonProperty(value="notificationType",required=true)String notificationType,
#JsonProperty(value="content",required=true)String content, #JsonProperty(value="medium",required=true)String medium) {
super();
this.notificationType = notificationType;
this.content = content;
this.medium = medium;
}
#Override
public String toString() {
return "NotificationTemplate [id=" + id + ", templateId=" + templateId + ", notificationType="
+ notificationType + ", content=" + content + ", medium=" + medium + "]";
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Id
private String id;
private String templateId;
public String getTemplateId() {
return templateId;
}
public void setTemplateId(String templateId) {
this.templateId = templateId;
}
private String notificationType;
private String content;
private String medium;
public String getMedium() {
return medium;
}
public void setMedium(String medium) {
this.medium = medium;
}
public String getNotificationType() {
return notificationType;
}
public void setNotificationType(String notificationType) {
this.notificationType = notificationType;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
Controller where payload is posted.
#PostMapping(value = "/config", consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<NotificationTemplate> configureTemplate(
#Valid #RequestBody NotificationTemplate notificationTemplate) {
NotificationTemplate notificationTemplatePersisted = null;
logger.info(
"Printing payload of template on server side" + ">>>" + notificationTemplate.toString());
try {
validatePayLoad(notificationTemplate);
notificationTemplatePersisted =
notificationTemplateService.createNotificationTemplate(notificationTemplate);
} catch (Exception de) {
logger.info(String.format("Error in saving template", de.getMessage()));
throw new RequestNotCompletedException(de.getLocalizedMessage());
}
return new ResponseEntity<NotificationTemplate>(notificationTemplatePersisted,
HttpStatus.CREATED);
}
Question:How do I validate that an uknown property has been sent as part of payload.In Existing implementation,#RequestBody maps the json without any issue.I want to throw error or validate payload if incoming json is not confirming exactly to POJO.For e.g in payload example i gave,I want to be able to throw error saying that asa is not recognized property
The Jackson property that controls this behaviour is FAIL_ON_UNKNOWN_PROPERTIES. This needs to be true in your case, to get the behaviour you describe.
It seems that since spring boot 1.2 this is set to false by default.
To set it to true add this line to your application.properties file:
spring.jackson.deserialization.fail-on-unknown-properties=true
And then you will get a JsonMappingException when there are extraneous properties in a JSON payload
One can add this class int their project and it would throw an exception if json is mismatched to the pojo class properties.
#Configuration
public class Config implements InitializingBean {
#Autowired
private RequestMappingHandlerAdapter converter;
#Override
public void afterPropertiesSet() throws Exception {
configureJacksonToFailOnUnknownProperties();
}
private void configureJacksonToFailOnUnknownProperties() {
MappingJackson2HttpMessageConverter httpMessageConverter = converter.getMessageConverters().stream()
.filter(mc -> mc.getClass()
.equals(MappingJackson2HttpMessageConverter.class))
.map(mc -> (MappingJackson2HttpMessageConverter) mc)
.findFirst()
.get();
httpMessageConverter.getObjectMapper().enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}
}

Categories

Resources