How to make jsonData case insensitive and in Spring MVC - java

New to spring ,
i am trying to access json object in #RequestBody MYPOJO pojo which works fine , but my json data needed to be same as variable name in pojo and case sensitive. best i did find from web is here , but not synchronize with my project , i am using spring mvc. So how can i make case insensitive my json with pojo?
the way i receive json
#RequestMapping(value = "create", method = RequestMethod.POST)
public void createPost(HttpServletRequest req, HttpServletResponse resp, #Valid #RequestBody Post post,
Errors errors) throws CustomException, IOException {
json data
function jsonForPost(isEdit, id) {
var post = {};
if (isEdit) {
post.id = id;
}
post.name = $("#name").val();
return JSON.stringify(post);
}

With Spring Boot
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import com.fasterxml.jackson.databind.MapperFeature;
#Configuration
class Configs {
#Bean
public Jackson2ObjectMapperBuilderCustomizer initJackson() {
Jackson2ObjectMapperBuilderCustomizer c = new Jackson2ObjectMapperBuilderCustomizer() {
#Override
public void customize(Jackson2ObjectMapperBuilder builder) {
builder.featuresToEnable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
}
};
return c;
}
}
Without Spring Boot
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.fasterxml.jackson.databind.MapperFeature;
#Configuration
#EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.indentOutput(true);
builder.featuresToEnable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
}
I have a POJO with a variable name in it:
public class Pox {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and a Controller:
#RequestMapping(value = "/create", method = RequestMethod.POST)
public void createPost(HttpServletRequest req, HttpServletResponse resp, #Valid #RequestBody Pox post,
Errors errors) {
System.out.println(post.getName());
}
I have tested with Postman with:
name, Name, NAme, nAme.
All of them worked.

With springboot using the application.yml file =>
spring:
jackson:
mapper:
accept-case-insensitive-properties: true

Related

org.springframework.web.client.RestClientException: Error while extracting response for type and content type [application/json;charset=utf-8]

I can't figure out what the problem is. I am using postgre DB. When I run a test for the GET method, an error occurs, for the second day I can not solve it.
Here is my Entity class
import lombok.*;
import lombok.experimental.FieldDefaults;
import javax.persistence.*;
#Entity
#Table(name = "cities_catalog")
#FieldDefaults(level = AccessLevel.PRIVATE)
#Data
#NoArgsConstructor
public class PostgreCity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;
#Column(name = "name")
String name;
public PostgreCity(String name) {
this.name = name;
}
}
here is my Repository class
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface CityRepository extends JpaRepository<PostgreCity, Integer> {
}
here is my Controller class
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.experimental.FieldDefaults;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#RestController
#AllArgsConstructor
#FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class CityPostController {
#Autowired
CityRepository cityRepository;
#GetMapping(value = "/get")
public List<PostgreCity> get(){
List<PostgreCity> list = this.cityRepository.findAll();
return list;
}
}
here is my Junit test class
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import java.util.HashMap;
import static org.junit.jupiter.api.Assertions.*;
class CityPostControllerTest extends RequestService {
#Autowired
CityRepository cityRepositoryp;
Integer id;
#BeforeEach
void setUp() {
}
#AfterEach
void tearDown() {
}
#Test
void get() {
ResponseEntity<PostgreCity> responseEntity = this.get(PostgreCity.class);
assertNotNull(responseEntity);
assertEquals(HttpStatus.OK.value(), responseEntity.getStatusCodeValue());
}
#Override
public String getPath() {
return "/get";
}
}
here is my RequestService class
import io.egrow.eugene.insurance.InsuranceApplicationTests;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.*;
public abstract class RequestService extends InsuranceApplicationTests {
#Autowired
TestRestTemplate testRestTemplate;
public <T> ResponseEntity<T> patchNoAuth(String payload, Class<T> tClass) {
HttpHeaders headers = getHeaderWithoutAuthentication();
HttpEntity<String> entity = new HttpEntity<>(payload, headers);
return testRestTemplate.exchange(this.getPath(), HttpMethod.PATCH, entity, tClass);
}
public <T> ResponseEntity<T> get(Class<T> tClass) {
return testRestTemplate.getForEntity(this.getPath(), tClass);
}
private HttpHeaders getHeaderWithoutAuthentication() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
public abstract String getPath();
}
here is error message when I run test.
org.springframework.web.client.RestClientException: Error while extracting response for type [class io.egrow.eugene.insurance.boundary.databases.postgre.models.cities.PostgreCity] and content type [application/json;charset=utf-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `io.egrow.eugene.insurance.boundary.databases.postgre.models.cities.PostgreCity` from Array value (token `JsonToken.START_ARRAY`); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `io.egrow.eugene.insurance.boundary.databases.postgre.models.cities.PostgreCity` from Array value (token `JsonToken.START_ARRAY`)
at [Source: (PushbackInputStream); line: 1, column: 1]
The problem is here:
ResponseEntity<PostgreCity> responseEntity = this.get(PostgreCity.class);
You are expecting a single entity but in the RestController you have a List:
#GetMapping(value = "/get")
public List<PostgreCity> get(){
List<PostgreCity> list = this.cityRepository.findAll();
return list;
}
For getting the list you can use the ParameterizedTypeReference, like so:
ResponseEntity<List<PostgreCity>> responseEntity =
restTemplate.exchange(
"/get",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<PostgreCity>>() {}
);
List<PostgreCity> postgreCities = responseEntity.getBody();
For more details and reference can have a look at this tutorial:
https://www.baeldung.com/spring-resttemplate-json-list

How to provide completely custom JSON for example in Swagger?

I have Java endpoint which receives json-deserializable object. Unfortunately, Swagger is unable to auto-generate good example for it. Is it possible to provide completely custom JSON for an example?
Example is below, regard class Body. It has two fields.
One field is a Set. I want to provide some example list of values for it. I can't use example parameter for this.
Another field is a Parent. It can contain one of two of subclessed, Child1 and Child2. Springfox generates me
{
"parent": {
"#child#": "string"
},
"tags": "[\"tag1\", \"tag2\"]"
}
and I can't send this value (it's incorrect serialization). While I want to have
{
"parent": {
"#child#": "1",
"field1": "value of field 1"
},
"tags": ["tag1", "tag2"]
}
The code:
package com.example.demo;
import java.io.IOException;
import java.util.Set;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
#RestController
#SpringBootApplication
#Configuration
#EnableOpenApi
public class DemoApplication {
#PostMapping(value = "/create", consumes = MediaType.APPLICATION_JSON_VALUE)
public Body create(#RequestBody Body body) {
return body;
}
#Bean
public Docket docket() {
return new Docket(DocumentationType.OAS_30)
.select()
.apis(RequestHandlerSelectors.basePackage(DemoApplication.class.getPackageName()))
.paths(PathSelectors.any())
.build()
//.apiInfo(apiInfo())
//.securitySchemes(Collections.singletonList(apiKey()))
//.protocols(getProtocols(systemSettings))
;
}
public static class Body {
#ApiModelProperty(example = "[\"tag1\", \"tag2\"]")
public Set<String> tags;
public Parent parent;
}
#JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, property = "#child#", include = JsonTypeInfo.As.EXISTING_PROPERTY, visible = true)
#JsonTypeIdResolver(MyTypeIdResolver.class)
#ApiModel(discriminator = "#child#")
public static class Parent {
final String childTypeNumber;
#JsonProperty("#child#")
public String childTypeNumber() {
return childTypeNumber;
}
public Parent(String childTypeNumber) {
this.childTypeNumber = childTypeNumber;
}
}
public static class MyTypeIdResolver extends TypeIdResolverBase {
private JavaType superType;
#Override
public void init(JavaType baseType) {
superType = baseType;
}
#Override
public String idFromValue(Object value) {
return null;
}
#Override
public String idFromValueAndType(Object value, Class<?> suggestedType) {
return null;
}
#Override
public JsonTypeInfo.Id getMechanism() {
return null;
}
#Override
public JavaType typeFromId(DatabindContext context, String id) throws IOException {
char c = id.charAt(0);
Class<?> subType = null;
switch (c) {
case '1':
subType = Child1.class;
break;
case '2':
subType = Child2.class;
break;
default:
throw new RuntimeException("Invalid Child type");
}
return context.constructSpecializedType(superType, subType);
}
}
public static class Child1 extends Parent {
public String field1;
public Child1() {
super("1");
}
}
public static class Child2 extends Parent {
public String field2;
public Child2() {
super("2");
}
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
From what I understand, you want swagger to display the resource returned by the endpoint.
If so, this is the solution:
#Operation(summary = "create new resource",
description = "create resourcey completely", responses = {
#ApiResponse(responseCode = "200",
description = "createresource",
content = {#Content(mediaType = "application/json",
schema = #Schema(implementation = Body.class))})
#PostMapping(value = "/create", consumes = MediaType.APPLICATION_JSON_VALUE)
public Body create(#RequestBody Body body) {
return body;
}
So that the controller does not have so many things left, what is done is to create the controller interface with all the annotations on the method signature, then your controller will implement the interface that already has all the documentation annotations.

Spring Boot gives different response while load test(50 users) time

We have upgraded the project technology from Java servlet to Spring Boot. It's working fine for single user transaction mode, but it's not working multiple users test.
My Project configuration
UI - angular
Business Logic- Java Spring Boot
back end - No SQL get call
No hibernate
Session - stateless session
We don't process based on the user's session. We process only employee id and case id(75 % get request, 25 % post request). it's working fine for single user time. LoadNinja load test run 50 users time - we will get another user's response to me.
My name is Apple if I am requesting for my details(load ninja run the same process for 50 user test simultaneously) to the server , but i am getting response as Orange user's details. Sometimes I am getting a Null pointer exception.
I am unable to trace the cause of the issue - Could you please advise for project configuration changes.
Thanks in advance.
EmployeeController.java
package com.apa.documentprocessor.constants;
import java.io.IOException;
import javax.xml.bind.JAXBException;
import org.apache.commons.httpclient.HttpException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
#RequestMapping("/employee")
public class EmployeeController {
#Autowired
EmployeeService employeeservice;
#Autowired
Employee employee;
#RequestMapping("/getDetails")
public void getEmployeeDetails() throws HttpException, IOException, JAXBException {
employee.setEmpId("100");
employee.setEmpName("Nuno");
employee.setDept("Research");
employee.setLocation("India");
EmployeePerformance emp = employeeservice.EmployeeDetails(employee);
System.out.println(emp.toString());
}
}
EmployeeService.java
package com.apa.documentprocessor.constants;
import java.io.IOException;
import java.io.StringReader;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.stereotype.Component;
import org.xml.sax.InputSource;
#Component
public class EmployeeService {
#Autowired
EmployeePerformance empPerformance;
#Autowired
private AutowireCapableBeanFactory beanFactory;
public EmployeePerformance EmployeeDetails(Employee emp) throws HttpException, IOException, JAXBException {
this.empPerformance = beanFactory.createBean(EmployeePerformance.class);
this.empPerformance = getDetails(emp);
return empPerformance;
}
private EmployeePerformance getDetails(Employee emp) throws HttpException, IOException, JAXBException {
String result;
String url=emp.getEmpName();
HttpClient client = new HttpClient();
GetMethod method = new GetMethod(url);
method.addRequestHeader("3454362523", emp.getEmpId());
client.executeMethod(method);
result = method.getResponseBodyAsString();
JAXBContext jaxbContext = JAXBContext.newInstance(EmployeePerformance.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
EmployeePerformance empPerformance = (EmployeePerformance) jaxbUnmarshaller
.unmarshal(new InputSource(new StringReader(result)));
empPerformance.setProject("Banking");
empPerformance.setRating("Good");
return empPerformance;
}
}
EmployeePerformance.java
package com.apa.documentprocessor.constants;
import org.springframework.stereotype.Component;
#Component
public class EmployeePerformance {
String empName;
String rating;
String project;
String position;
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getRating() {
return rating;
}
public void setRating(String rating) {
this.rating = rating;
}
public String getProject() {
return project;
}
public void setProject(String project) {
this.project = project;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
}
Employee.java
package com.apa.documentprocessor.constants;
import org.springframework.stereotype.Component;
#Component
public class Employee {
String empId;
String empName;
String dept;
String location;
public String getEmpId() {
return empId;
}
public void setEmpId(String empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
Your code is flawed, don't make Employee nor EmployeePerformance spring managed beans. Just construct a new instance when you need one. The fact that one uses Spring doesn't mean everything needs to be managed by Spring.
#RequestMapping("/employee")
public class EmployeeController {
#Autowired
EmployeeService employeeservice;
#RequestMapping("/getDetails")
public void getEmployeeDetails() throws HttpException, IOException, JAXBException {
Employee employee = new Employee();
employee.setEmpId("100");
employee.setEmpName("Nuno");
employee.setDept("Research");
employee.setLocation("India");
EmployeePerformance emp = employeeservice.EmployeeDetails(employee);
System.out.println(emp.toString());
}
}
Regarding the use of HttpClient don't construct one each time, instead reuse it, or even better use RestTemplate which does all of that out-of-the-box, including the marshalling.
#Component
public class EmployeeService {
private final RestTemplate http;
public EmployeeService(RestTemplate http) {
this.http=http;
}
public EmployeePerformance EmployeeDetails(Employee emp) throws HttpException, IOException, JAXBException {
return getDetails(emp);
}
private EmployeePerformance getDetails(Employee emp) throws HttpException, IOException, JAXBException {
String url=emp.getEmpName();
RequestEntity req = RequestEntity.get(url).header("3454362523", emp.getEmpId()).build();
EmployeePerformance empPerformance = http.exchange(req, EmployeePerformance.class).getBody();
empPerformance.setProject("Banking");
empPerformance.setRating("Good");
return empPerformance;
}
}
In your configuration you need to add the following
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.requestFactory(HttpComponentsClientHttpRequestFactory.class).build();
}
With this you are not sharing state anymore and reusing heavy objects instaed of constructing them each time you need them.

com.fasterxml.jackson.databind.exc.InvalidDefinitionException While calling an Rest API from Spring boot Application

Hi I am running two Spring boot Applications. From one Application I am trying to access Rest API of another application using RestTemplate.
This is my API present in first Spring boot which I am trying to call from my second Spring boot Application.
MovieControllerData.java
package com.movies.pinku;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/movies")
public class MovieControllerData {
#RequestMapping("/{movieId}")
public Movie getMovieData(#PathVariable("movieId") String movieId) {
return new Movie("1223", "test123");
}
}
Movie.java
package com.example.MicroServices.MicroServicesSpringBoot;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown = true)
public class Movie {
private String movieId;
public Movie(String movieId, String name) {
super();
this.movieId = movieId;
this.name = name;
}
private String name;
public String getMovieId() {
return movieId;
}
public void setMovieId(String movieId) {
this.movieId = movieId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
This is another class in second Spring boot application where I am trying to call first Rest API
package com.example.MicroServices.MicroServicesSpringBoot;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
#RestController
#RequestMapping("/catalog")
public class MovieController {
#RequestMapping("/{userId}")
public List<CatalogItem> getCataLog(#PathVariable("userId") String userId){
final String uri = "http://localhost:8083/movies/{movieId}";
Map<String, String> params = new HashMap<String, String>();
params.put("movieId", "foo");
RestTemplate restTemplate = new RestTemplate();
Movie movie = restTemplate.getForObject(uri, Movie.class , params);
System.out.println("Data in movie is=============:"+ movie);
return Collections.singletonList(new CatalogItem(movie.getName(), "Action", movie.getMovieId()));
}
}
I am getting this error
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of com.example.MicroServices.MicroServicesSpringBoot.Movie (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (PushbackInputStream); line: 1, column: 2]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67) ~[jackson-databind-2.10.0.jar:2.10.0]

SpringBoot: Consume & Produce XML with a Custom Serializer + Deserializer

I have a SpringBoot Service with:
Model
public class Payload {
private final String id;
public Payload(String id){
this.id = id;
}
public String getId() {
return this.id;
}
}
Controller
#RestController
#RequestMapping("/payload")
public class PayloadController {
#RequestMapping(method = RequestMethod.POST)
public Payload post(#RequestBody final Payload payload) {
return payload;
}
}
I need this Controller to be able to handle JSON & XML requests and respond with the same format.
This works fine providing I set the Content-Type and Accept headers to the correct media types.
However, my XML payloads need to be in a subtly different structure to my JSON:
XML:
<Payload>
<id value="some-value"/>
</Payload>
JSON:
{
id: "some-value"
}
How do I ensure my id is wrapped in an xml node and has the "value" as an attribute?
I have tried using a #JsonSerialize and #JsonDeserialize annotation on my Payload class but as soon as I do this I get the following error when POSTing XML
{
"timestamp": "2019-10-01T12:06:35.593+0000",
"status": 415,
"error": "Unsupported Media Type",
"message": "Content type 'application/xml;charset=UTF-8' not supported",
"path": "/payload"
}
You need to register 2 converters:
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter for JSON.
org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter for XML.
Because, Payload class fits JSON payload you need to add only JsonCreator and JsonProperty annotations to make it work:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Payload {
private final String id;
#JsonCreator
public Payload(#JsonProperty(value = "id") String id) {
this.id = id;
}
public String getId() {
return this.id;
}
}
XML payload does not fit by default, so we need to implement custom serialiser:
import com.example.demo.model.Payload;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import java.io.IOException;
public class PayloadXmlSerializer extends JsonSerializer<Payload> {
#Override
public void serialize(Payload value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
ToXmlGenerator toXmlGenerator = (ToXmlGenerator) gen;
toXmlGenerator.writeStartObject();
toXmlGenerator.writeObjectFieldStart("id");
toXmlGenerator.setNextIsAttribute(true);
toXmlGenerator.writeFieldName("value");
toXmlGenerator.writeString(value.getId());
toXmlGenerator.setNextIsAttribute(false);
toXmlGenerator.writeEndObject();
toXmlGenerator.writeEndObject();
}
}
and deserialiser:
import com.example.demo.model.Payload;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonPointer;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.node.TextNode;
import java.io.IOException;
public class PayloadXmlDeserializer extends JsonDeserializer<Payload> {
#Override
public Payload deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
TreeNode root = p.readValueAsTree();
TreeNode value = root.at(JsonPointer.compile("/id/value"));
if (value.isMissingNode()) {
return new Payload(null);
}
TextNode textNode = (TextNode)value;
return new Payload(textNode.textValue());
}
}
Finally, we need to register above HTTP converters and custom serialiser/deserialiser:
import com.example.demo.model.Payload;
import com.example.jackson.PayloadXmlDeserializer;
import com.example.jackson.PayloadXmlSerializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
#EnableWebMvc
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//JSON
converters.add(new MappingJackson2HttpMessageConverter());
// XML
converters.add(new MappingJackson2XmlHttpMessageConverter(Jackson2ObjectMapperBuilder
.xml()
.modules(payloadModule())
.build()));
}
public SimpleModule payloadModule() {
SimpleModule module = new SimpleModule();
module.addDeserializer(Payload.class, new PayloadXmlDeserializer());
module.addSerializer(Payload.class, new PayloadXmlSerializer());
return module;
}
}
See also:
Using Jackson to add XML attributes to manually-built node-tree
415 Unsupported MediaType for POST request in spring application
Spring MVC

Categories