I'm trying to send an instance of the object EGiftCreationRequest as JSON via POST body in Spring:
final BigDecimal amount = new BigDecimal(100.00);
final String configurationId = "test_configuration_id";
final String referenceNumber = "12345";
EGiftCreationRequest giftCreationRequest = new EGiftCreationRequest() {{
giftAmount(amount);
productConfigurationId(configurationId);
retrievalReferenceNumber(referenceNumber);
}};
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<EGiftCreationRequest> httpEntity = new HttpEntity<EGiftCreationRequest>(giftCreationRequest, headers);
ResponseEntity<EGift> entity = new TestRestTemplate().postForEntity(
"http://localhost:" + this.port + "/eGiftProcessing/v1/generateEGift",
httpEntity,
EGift.class
);
However, for some reason the object is being serialized into the following String:
{"headerParams":{}}
Obviously this has nothing to do with my EGiftCreationRequest, which is actually:
public class EGiftCreationRequest extends RequestBase<EGiftCreationRequest> {
private BigDecimal giftAmount;
private String productConfigurationId;
private String retrievalReferenceNumber;
public BigDecimal giftAmount() {
return this.giftAmount;
}
public String productConfigurationId() {
return this.productConfigurationId;
}
public String retrievalReferenceNumber() {
return this.retrievalReferenceNumber;
}
public EGiftCreationRequest giftAmount(final BigDecimal giftAmount) {
this.giftAmount = giftAmount;
return this;
}
public EGiftCreationRequest productConfigurationId(final String productConfigurationId) {
this.productConfigurationId = productConfigurationId;
return this;
}
public EGiftCreationRequest retrievalReferenceNumber(final String retrievalReferenceNumber) {
this.retrievalReferenceNumber = retrievalReferenceNumber;
return this;
}
}
What can possibly be going on?
This is caused by a misconfigured Jackson mapper. By default, Jackson is looking for accessors named in JavaBeans fashion (get*(), set*()) to retrieve and set values. Since the model uses a different naming convention (the field names themselves), Jackson fails to serialize the object.
The following mapper configuration makes everything work correctly:
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
mapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
mapper.setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE);
mapper.setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.NONE);
TestRestTemplate testRestTemplate = new TestRestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
messageConverters.add(new MappingJackson2HttpMessageConverter(mapper));
testRestTemplate.setMessageConverters(messageConverters);
Related
I have few child entities that i want to send to a single API endpoint with seperate API requests, so i wrap them in a parent entity and send it as below.
Main wrapper class
#Getter
#Setter
public class ProfileUpdateRequest<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
}
#Getter
#Setter
public class EmailPreferencesUpdateRequest {
#JsonProperty
private boolean isAgreedToEmails;
}
#Getter
#Setter
public class AddressUpdateRequest {
private List<UserAddress> addresses;
}
Send update email preference request
#RequestMapping(value = "/update-email", method = RequestMethod.POST, headers = "Accept=*/*", produces = "application/json")
#ResponseBody
public String updateEmailNotificationPreferences(#RequestBody EmailPreferencesUpdateRequest emailPreferencesUpdateRequest,
HttpServletRequest request) {
ProfileUpdateRequest<EmailPreferencesUpdateRequest> updateRequest = new ProfileUpdateRequest<>();
updateRequest.add(emailPreferencesUpdateRequest);
UriComponents uri = UriComponentsBuilder
.fromHttpUrl(baseUrl + "/user/update")
.build();
String urlString = uri.toUriString();
HttpHeaders requestHeaders = new HttpHeaders();
RestTemplate restTemplate = new RestTemplate();
HttpEntity<ProfileUpdateRequest> requestWithHeader = new HttpEntity<>(updateRequest, requestHeaders);
restTemplate.exchange(urlString, HttpMethod.PUT, requestWithHeader, UserEntry.class);
return "success";
}
Send update addresses request
#RequestMapping(value = "/update-addresses", method = RequestMethod.POST, headers = "Accept=*/*", produces = "application/json")
#ResponseBody
public String updateAddresses(#RequestBody List<UserAddress> userAddresses, HttpServletRequest request) {
AddressUpdateRequest addressUpdateRequest = new AddressUpdateRequest();
addressUpdateRequest.setAddresses(userAddresses);
ProfileUpdateRequest<AddressUpdateRequest> updateRequest = new ProfileUpdateRequest<>();
updateRequest.add(addressUpdateRequest);
UriComponents uri = UriComponentsBuilder
.fromHttpUrl(baseUrl + "/user/update")
.build();
String urlString = uri.toUriString();
HttpHeaders requestHeaders = new HttpHeaders();
RestTemplate restTemplate = new RestTemplate();
HttpEntity<ProfileUpdateRequest> requestWithHeader = new HttpEntity<>(updateRequest, requestHeaders);
restTemplate.exchange(urlString, HttpMethod.PUT, requestWithHeader, UserEntry.class);
return "success";
}
API endpoint
#Path("/user/update")
#PUT
public UserEntry updateUser(ProfileUpdateRequest profileUpdateRequest) {
if (profileUpdateRequest.get() instanceof AddressUpdateRequest) {
// update addresses
} else if (profileUpdateRequest.get() instanceof EmailPreferencesUpdateRequest) {
// update emails
}
}
From the endpoint i need to get the entity as in ProfileUpdateRequest type and check the instance what type of entity i have wrapped so i can pass to the relevant operation. But im getting it as a LinkedHashMap. How to resolve this issue?
There are 2 ways -
Either convert profileUpdateRequest.get() , to one of the Object if works thats fine , if not you will get the exception, then convert to other one.
Your linkedHashMap will contain the field in the object i.e. for EmailPreferencesUpdateRequest it will contain a key like "isAgreedToEmails", check in the Map with if else, and , then using ObjectMapper do below -
EmailPreferencesUpdateRequest emailRequest = new ObjectMapper().readValue(YourMap, EmailPreferencesUpdateRequest.class);
According to third party API spec, I need to send null value in JSON using ObjectMapper if no value exists,
Expected results : "optional": null
If optional value exists, then send "optional": "value"
I didn't find such option in Jackson – Working with Maps and nulls
Code:
requestVO = new RequestVO(optional);
ObjectMapper mapper = new ObjectMapper();
String requestString = mapper.writeValueAsString(requestVO);
Class:
public class RequestVO {
String optional;
public RequestVO(String optional) {
this.optional = optional;
}
public String getOptional() {
return optional;
}
public void setOptional(String optional) {
this.optional= optional;
}
Add #JsonInclude(JsonInclude.Include.USE_DEFAULTS) annotation to your class.
#JsonInclude(JsonInclude.Include.USE_DEFAULTS)
class RequestVO {
String optional;
public RequestVO(String optional) {
this.optional = optional;
}
public String getOptional() {
return optional;
}
public void setOptional(String optional) {
this.optional = optional;
}
}
Example :
RequestVO requestVO = new RequestVO(null);
ObjectMapper mapper = new ObjectMapper();
try {
String requestString = mapper.writeValueAsString(requestVO);
System.out.println(requestString);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
Output :
{"optional":null}
With value:
RequestVO requestVO = new RequestVO("test");
ObjectMapper mapper = new ObjectMapper();
try {
String requestString = mapper.writeValueAsString(requestVO);
System.out.println(requestString);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
Output:
{"optional":"test"}
You can use #JsonInclude annotation on even properties. So, this way you can either serialize as null or ignore some of the properties while serializing.
You can configure your ObjectMapper this way:
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
If no value is present on the JSON request your processed will have a null as you expected.
You can even configure a Spring bean for the ObjectMapper if you need.
EDIT:
I misunderstood the question, he was interested on the JSON response and not on the object parsed. The correct property is this case is JsonInclude.Include.USE_DEFAULTS.
Apologies for the confusion.
When I use object mapper, it inluces \r\n in the responses.Help me how to resolve it.
I am having train POJO and it has String name and String Value.
I set name as "Sydney" and Value as "SYD".It reruns
{\ \ \"name \" : \"Sydney\",\ \ \"Value \" : \"SYD\",\ \ \"isEnable\" : false,\ \ \"isCurrent\" : false\ \ }"
raw value in browser
"{\r\n \"name\" : \"Sydney\",\r\n \"name\" : \"SYD\",\r\n \"isEnable\" : false,\r\n \"isCurrent\" : false\r\n}"
below is my code
Train
public class Train {
public Train() {
}
private String name;
private String value;
private String Code;
private String countryName;
private String state;
private String stateName;
private boolean isEnable;
private boolean isCurrent;
//*getters and setters/*/
}
Controller calss
public ResponseEntity<String> getDetails( )
throws IOException {
ResponseEntity<String> responseEntity = null;
try(StringWriter writer = new StringWriter()) {
ObjectMapper mapper = new ObjectMapper();
Train train = new Train();
// set name and value to the train object//
if(train != null)
{
mapper.setSerializationInclusion(Inclusion.NON_NULL);
mapper.setSerializationInclusion(Inclusion.NON_EMPTY);
mapper.writerWithDefaultPrettyPrinter().writeValue(writer,
train);
responseEntity = new ResponseEntity<>(writer.toString(),
HttpStatus.OK);
}
}
catch()
{}
return responseEntity;
}
Configuration:
#Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
converters.add(extendedJsonConvertor());
super.configureMessageConverters(converters);
}
#Bean
public MappingJackson2HttpMessageConverter extendedJsonConvertor() {
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter
.setObjectMapper(getNullAndEmptyFilteredObjectMapper());
return mappingJackson2HttpMessageConverter;
}
#Bean
public ObjectMapper getNullAndEmptyFilteredObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
objectMapper.configure(
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return objectMapper;
}
When I debug the above code I came to know mapper include those \r\n in the response.Help me how to remove those slashes.
The problem is the line below.
mapper.writerWithDefaultPrettyPrinter().writeValue(writer,train);
Try replacing it with
mapper.writeValue(writer,train);
Why do you create a new object mapper when you are configuring, MappingJackson2HttpMessageConverter?
You can autowire the object mapper or return the actual object and let spring convert it to json
That's a "simple" double encoding issue i believe. You set a string in the response entity which is again writen as a json response.
If you want to rely on the spring view rendering (mappingjackson2httpmessageconverter) you have to create a response entity for "Train". (Or return a train instance directly from your controller method)
Or you use the way you implemented it and you have to ensure that rendering a string for a json response will not use the jackson message converter, but is left untouched by spring.
I am using Spring framework to get JSON data from a local server into an Object via Http GET.
But the object is always null(no data stored)
I have double checked the server and it is working fine
the server returns {"Propid":"61", "Proptitle":"3 bhk villa","Propdealer":"admin"}
I have added the Jackson Libraries
I have used StringHttpMessageConverter and it returns the JSON string {"Propid":"61", "Proptitle":"3 bhk villa","Propdealer":"admin"}
Throws exception:Could not extract response: no suitable HttpMessageConverter found for response type [com.aditya.master.classes.Prop] and content type [text/html;charset=UTF-8]
Here is the code that parses the JSON response
URI targetUrl= UriComponentsBuilder.fromUriString("http://192.168.1.9/PinSpace/oauth/")
.path("request_access/")
.queryParam("query", "get_property")
.queryParam("access_token", auth_code)
.queryParam("prop_id", "61")
.build()
.toUri();
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAccept(Collections.singletonList(new MediaType("application", "json")));
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
ResponseEntity<Prop> responseEntity = restTemplate.exchange(targetUrl, HttpMethod.GET, requestEntity, Prop.class);
Prop result = responseEntity.getBody();
Here is the Prop class
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonIgnoreProperties(ignoreUnknown = true)
public class Prop {
#JsonProperty
private String Propid, Proptitle, Propdealer;
public String getPropid() {
return Propid;
}
public void setPropid(String propid) {
Propid = propid;
}
public String getProptitle() {
return Proptitle;
}
public void setProptitle(String proptitle) {
Proptitle = proptitle;
}
public String getPropdealer() {
return Propdealer;
}
public void setPropdealer(String propdealer) {
Propdealer = propdealer;
}
}
Please suggest a solution
Thanks!
You can test deserialization with follow code:
ObjectMapper objectMapper = new ObjectMapper();
String content = "{\"Propid\":\"61\", \"Proptitle\":\"3 bhk villa\",\"Propdealer\":\"admin\"}";
objectMapper.readValue(content , Prop.class);
This trows exeception
org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "Propid"
which means that fields naming in your class is incorrect or you need to point correct names in #JsonProperty annotation
I suggest you to use next structure:
public class Prop {
private String propid;
private String proptitle;
private String propdealer;
public String getPropid() {
return propid;
}
#JsonProperty("Propid")
public void setPropid(String propid) {
this.propid = propid;
}
public String getProptitle() {
return proptitle;
}
#JsonProperty("Proptitle")
public void setProptitle(String proptitle) {
this.proptitle = proptitle;
}
public String getPropdealer() {
return propdealer;
}
#JsonProperty("Propdealer")
public void setPropdealer(String propdealer) {
this.propdealer = propdealer;
}
}
There is a way to get this to work with an incorrect MIME type as well: you just need to add "text/html" to your list of accepted media types. like so:
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
List<MediaType> mediaTypeList = new ArrayList<MediaType>();
//...
mediaTypeList.addAll( jsonConverter.getSupportedMediaTypes() );
mediaTypeList.add(MediaType.TEXT_HTML);
jsonConverter.setSupportedMediaTypes(mediaTypeList);
this will be quite handy if you don't have access to the server.
NOTE
there's probably a less verbose way to do this, but I'm just getting back to Java after 10 years in other environs :-)
Can someone help me to figure out what's need to be added?
JSON :
{"value":{"keyword":"better","correct":"","page":0,"size":10,"cost":51,"total":1107}}
Object class
#JsonAutoDetect
#JsonSerialize(include = Inclusion.NON_NULL)
#JsonRootName(value = "value")
public class Response {
private int page;
private int size;
private int total;
private int cost;
private int result;
private String keyword;
private String correct;
Still it gets the "Servlet.service() for servlet appServlet threw exception
org.codehaus.jackson.map.exc.UnrecognizedPropertyException:
Unrecognized field "value" (), not marked as ignorable"
Try adding this to your mapper config
mapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
mapper.configure(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE, true);
If you use RestTemplate you will need to configure the underlying jackson mapper. You can do this by configuring your mapper and setting it in the converter. See code below.
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
mapper.configure(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE, true);
MappingJacksonHttpMessageConverter messageConverter = new MappingJacksonHttpMessageConverter();
messageConverter.setObjectMapper(mapper);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
messageConverters.add(messageConverter);
RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(messageConverters);
See here for more details: https://jira.springsource.org/browse/ANDROID-45