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 :-)
Related
Below is Rest Controller class, which is post request
#PostMapping(value = "/details")
public SubSeriesDetailsRequest getGlobalSearchProductDetails(#RequestBody List<SubSeriesCodesRequest> codes,
#QueryParam("contextString") String contextString) {
LOGGER.info("Inside post request " + codes);
String url = "http://www.example.com/details";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Object> entity = new HttpEntity<>(headers);
// SubSeriesDetailsRequest detailsRequest = new SubSeriesDetailsRequest();
SubSeriesDetailsRequest response = restTemplate.postForObject(url, entity, SubSeriesDetailsRequest.class);
return response;
}
my dto class
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class SubSeriesCodesRequest implements Serializable {
private static final long serialVersionUID = 6880035785505252493L;
private List<String> codes;
public SubSeriesCodesRequest(List<String> codes) {
super();
this.codes = codes;
}
#JsonCreator
public SubSeriesCodesRequest() {
}
public List<String> getCodes() {
return codes;
}
public void setCodes(List<String> codes) {
this.codes = codes;
}
need to implement post request like this below
Your request does not match the expected model. You are saying you expect a List of SubSeriesCodesRequest and you are sending an array of strings.
Your current model seems to be like this example:
[
{
"codes": [
"Code 1",
"Code 2"
]
}
]
Try changing your method to something like this public SubSeriesDetailsRequest getGlobalSearchProductDetails(#RequestBody SubSeriesCodesRequest request, and it should work ok.
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);
I have created an API with a Map<String, Integer> parameter, like this:
#RequestMapping(value = "upload", method = RequestMethod.POST)
public ResponseEntity<String> handleContactsFileUpload(#RequestParam("file") MultipartFile file,
#RequestParam("name") String name,
#RequestParam("campaignAppItemId") Long campaignAppItemId,
#RequestParam("fileColumnHeaders") Map<String,Integer> fileColumnHeaders) throws Exception {
if (file == null)
return new ResponseEntity<>("No file uploaded", HttpStatus.BAD_REQUEST);
contactService.handleContactsFile(file, name, campaignAppItemId,fileColumnHeaders);
return new ResponseEntity<>("File uploaded successfully", HttpStatus.OK);
}
I am trying to call this via Postman:
I passed the fileColumnHeaders inside Body->Form Data as in the screenshot.
Then I got a message like this in Postman:
Failed to convert value of type 'java.lang.String' to required type 'java.util.Map'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.util.Map': no matching editors or conversion strategy found.
Anybody know why this message came ?
How can we pass a map as a parameter in Rest API request?
How can we pass a map through Postman?
You could use #RequestBody instead of #RequestParam for Maps and other non trivial data types and objects - this way spring will map the JSON representing your map parameter to a domain object, which is then serializable and can be converted to a java object.
... Or simply create a converter:
#Component
#RequiredArgsConstructor
public class StringToMapConverter implements Converter<String, Map<String, Object>> {
private final ObjectMapper objectMapper;
#Override
public Map<String, Object> convert(String source) {
try {
return objectMapper.readValue(source, new TypeReference<Map<String, String>>() {
});
} catch (final IOException e) {
return null;
}
}
}
Firstly, you create DTO object to get all data from your request.
public class FormDataDTO {
private MultipartFile file;
private String name;
private Long campaignAppItemId;
private Map<String,Integer> fileColumnHeaders;
// getters, setters
}
Secondly, you can map FormDataDTO from your request without any annotation:
#RequestMapping(value = "upload", method = RequestMethod.POST)
public ResponseEntity<String> handleContactsFileUpload(FormDataDTO formDataDTO){
// your logic code here
}
Finally, form-data in your request will be:
I think this could work:
#RequestMapping(value = "upload/{fileColumnHeaders}", method = RequestMethod.POST)
public ResponseEntity<String> handleContactsFileUpload(#RequestParam("file") MultipartFile file,
#RequestParam("name") String name,
#RequestParam("campaignAppItemId") Long campaignAppItemId,
#MatrixVariable Map<String,Integer> fileColumnHeaders) throws Exception {
if (file == null)
return new ResponseEntity<>("No file uploaded", HttpStatus.BAD_REQUEST);
contactService.handleContactsFile(file, name, campaignAppItemId,fileColumnHeaders);
return new ResponseEntity<>("File uploaded successfully", HttpStatus.OK);
}
Put all other parameters into the body, but add the fileColumnHeaders to the URL like this:
/upload/firstName=1;lastName=2;address=3;phone=4
You will also need this extra configuration:
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
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);
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.