How to provide completely custom JSON for example in Swagger? - java

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.

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

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

getElementsAnnotatedWith method returns empty list while annotation processing

My goal is code generation via annotation processor. I want to generate new class on the top of the existent base class by excluding some fields according to annotations and adding some constarint validators etc. I have 3 modules. First one is base module which contains Car class and annotations BaseClass, A and B. Second module is annotation processor module. It contains CustomCodeGenerator annotaion and its processor. And third module is the module which I want to generate NewCar class onto it and use that NewCar class in it.
Car.Class
#BaseClass
public class Car {
#A
private int seatCount;
#B
private String name;
private String dummy;
public Car(int seatCount, String name) {
this.seatCount = seatCount;
this.name = name;
}
public int getSeatCount() {
return seatCount;
}
public void setSeatCount(int seatCount) {
this.seatCount = seatCount;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDummy() {
return dummy;
}
public void setDummy(String dummy) {
this.dummy = dummy;
}
}
CustomCodeGenerator.class
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.SOURCE)
#Target(ElementType.TYPE)
public #interface CustomCodeGenerator {
}
CustomCodeGeneratorProcessor.class
import com.squareup.javapoet.*;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.io.IOException;
import java.util.List;
import java.util.Set;
#SupportedAnnotationTypes("*")
public class CustomCodeGeneratorProcessor extends AbstractProcessor {
private Filer filer;
private Messager messager;
private Elements elementUtils;
private Types typeUtils;
#Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
filer = processingEnvironment.getFiler();
messager = processingEnvironment.getMessager();
elementUtils = processingEnvironment.getElementUtils();
typeUtils = processingEnvironment.getTypeUtils();
}
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (!roundEnv.processingOver()) {
try {
Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(BaseClass.class);
for (Element element : elementsAnnotatedWith) {
TypeElement element1 = (TypeElement) element;
List<? extends Element> enclosedElements = element1.getEnclosedElements();
MethodSpec main = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(Integer.class, "seatCount")
.addStatement("this.$N = $N", "seatCount", "seatCount")
.build();
TypeSpec.Builder builder = TypeSpec.classBuilder("NewCar")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(AnnotationSpec.builder(ClassName.get("", "ValidPassengerCount")).build())
.addMethod(main);
outer:
for (Element enclosedElement : enclosedElements) {
if (enclosedElement.getKind().isField()) {
List<? extends AnnotationMirror> annotationMirrors = enclosedElement.getAnnotationMirrors();
for (AnnotationMirror declaredAnnotation : annotationMirrors) {
if (!typeUtils.isSameType(elementUtils.getTypeElement("A").asType(), declaredAnnotation.getAnnotationType())) {
continue outer;
}
}
builder.addField(TypeName.get(enclosedElement.asType()), enclosedElement.getSimpleName().toString(), Modifier.PUBLIC);
}
}
JavaFile javaFile = JavaFile.builder("", builder.build())
.build();
javaFile.writeTo(filer);
}
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
#Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}
The third module is like below as well.
Main.class
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.Set;
#CustomCodeGenerator
public class Main {
public static void main(String[] args) {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
NewCar car = new NewCar(-1);
Set<ConstraintViolation<NewCar>> violationSet = validator.validate(car);
System.out.println(violationSet.iterator().next().getMessage());
}
}
ValidPassengerCount.class
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = {ValidPassengerCountValidator.class})
public #interface ValidPassengerCount {
String message() default "invalid passenger count!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
ValidPassengerCountValidator.class
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class ValidPassengerCountValidator
implements ConstraintValidator<ValidPassengerCount, NewCar> {
public void initialize(ValidPassengerCount constraintAnnotation) {
}
public boolean isValid(NewCar car, ConstraintValidatorContext context) {
if (car == null) {
return true;
}
return 0 <= car.seatCount;
}
}
The problem is roundEnv.getElementsAnnotatedWith(BaseClass.class) in CustomCodeGeneratorProcessor.class returns empty list. If I move Car into the 3rd module it works. However my goal is generating new code from the base class which comes from dependent module which is module 1 for this example. Is there any way to reach annotated elements of dependent module?

How to parse request parameter (query and path param) and request body in same POJO in REST API

I have a rest API (PUT verb) which accepts both request body and path params:
Ex:
curl --data {a:1, b:2} -X PUT "https://example.com/users/{username}/address/{addressname}"
I am trying to fetch both request body and path param in one POJO
Response myAPI(#BeanParam Users user){
system.out.println(user.username);
system.out.println(user.a);
Users class
public class Users{
#PathParam(username)
private String userName;
......
private String a;
......
}
But I am getting value of user.a as null.
How to parse both request body and param in same class?
You can do this with a custom annotation and an InjectionResolver. What the InjectionResolver does is allow you to create a custom injection point with your own annotation. So you could do something like
public class Users {
#PathParam(username)
private String userName;
#Body
private String a;
}
When implementing the InjectionResolver, you would grab the actual body from the ContainerRequest using the ContainerRequest#readEntity(Class) method. You would determine the Class to pass by doing some reflection on the Field that you can obtain inside the InjectionResolver. Below is a complete example using Jersey Test Framework. Run it like any other JUnit test.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.BeanParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import org.glassfish.hk2.api.Injectee;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
/**
* Only one required dependency to run this test. Note this is using 2.25.1.
* If you are using 2.26 or later, the implementation will be different,
* and this will not work. You need to use the Jersey packaged `InjectionResolver`,
* not the HK2 one. And you will also need to bind that `InjectionResolver`
* to `GenericType` instead of `TypeLiteral` in the `AbstractBinder#configure()`.
*
* <dependency>
* <groupId>org.glassfish.jersey.test-framework.providers</groupId>
* <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
* <version>2.25.1</version>
* <scope>test</scope>
* </dependency>
*/
public class BeanParamTest extends JerseyTest {
#Path("test")
#Consumes("application/json")
#Produces("application/json")
public static class TestResource {
#POST
#Path("{username}")
public String post(#BeanParam ModelBean bean) {
return bean.toString();
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig(TestResource.class)
.register(new AbstractBinder() {
#Override
protected void configure() {
bind(BodyInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<Body>>() {})
.in(Singleton.class);
}
});
}
#Test
public void testIt() {
final Response res = target("test/peeskillet")
.request()
.post(Entity.json("{\"foo\":\"bar\"}"));
System.out.println(res.readEntity(String.class));
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD})
public #interface Body {}
public static class ModelBean {
#PathParam("username")
private String username;
#Body
private String body;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
#Override
public String toString() {
return "ModelBean{" +
"username='" + username + '\'' +
", body='" + body + '\'' +
'}';
}
}
public static class BodyInjectionResolver implements InjectionResolver<Body> {
#Inject
private javax.inject.Provider<ContainerRequest> requestProvider;
#Override
public Object resolve(Injectee injectee, ServiceHandle<?> serviceHandle) {
if (injectee.getParent().isAnnotationPresent(Body.class)) {
AnnotatedElement parent = injectee.getParent();
if (parent instanceof Field) {
Class<?> entityType = ((Field) parent).getType();
return requestProvider.get().readEntity(entityType);
}
}
return null;
}
#Override
public boolean isConstructorParameterIndicator() {
return false;
}
#Override
public boolean isMethodParameterIndicator() {
return false;
}
}
}

How to make jsonData case insensitive and in Spring MVC

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

Categories