Cannot deserialize instance of <Object> out of START_ARRAY token - webClient - reactive-programming

I am writing a simple get method using webclient to fetch property information. But then, I am getting below's error response message:
{
"timestamp": "2019-02-25T06:57:03.487+0000",
"path": "/modernmsg/getentity",
"status": 500,
"error": "Internal Server Error",
"message": "JSON decoding error: Cannot deserialize instance of `com.reputation.api.modernmsg.model.Entity` out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `com.reputation.api.modernmsg.model.Entity` out of START_ARRAY token\n at [Source: UNKNOWN; line: -1, column: -1]"
}
Actual json response is:
[
{
"name": "Point Breeze",
"street": "488 Lemont Dr",
"city": "Nashville",
"state": "TN",
"postal_code": "37216",
"slug": "point-breeze"
}
]
Below is the method in my controller class to fetch property:
#RequestMapping(method = RequestMethod.GET, value = "/getentity")
public Mono<Entity> getEntity(#RequestParam("token") String token, #RequestParam("name") String name) {
return service.fetchEntity(token, name);
}
And my fetchEntity method is:
public Mono<Entity> fetchEntity(String token, String name) {
String url = host + version + entityEndpoint + "?token=" + token + "&name=" + name;
return webClient.get().uri(url).retrieve().bodyToMono(Entity.class);
}
Below is my Entity model:
package com.reputation.api.modernmsg.model;
import java.util.List;
public class Entity {
private List<ModernMsgEntity> modernMsgEntity;
public List<ModernMsgEntity> getModernMsgEntity() {
return modernMsgEntity;
}
public void setModernMsgEntity(List<ModernMsgEntity> modernMsgEntity) {
this.modernMsgEntity = modernMsgEntity;
}
}
ModernMsgEntity model is:
package com.reputation.api.modernmsg.model;
public class ModernMsgEntity {
private String name;
private String street;
private String city;
private String state;
private String postal_code;
private String slug;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getPostal_code() {
return postal_code;
}
public void setPostal_code(String postal_code) {
this.postal_code = postal_code;
}
public String getSlug() {
return slug;
}
public void setSlug(String slug) {
this.slug = slug;
}
}
Let me know if you need more information.

This is more of a JSON deserialization problem. Looking at your entity class, you're setting things up to expect a JSON response like:
{
"modernMsgEntity": [
{
"name": "Point Breeze",
"street": "488 Lemont Dr",
"city": "Nashville",
"state": "TN",
"postal_code": "37216",
"slug": "point-breeze"
}
]
}
If you want Jackson to deserialize an array of objects directly, you have to tell it so:
Flux<ModernMsgEntity> messages = webClient.get().uri(url).retrieve().bodyToFlux(ModernMsgEntity.class);

Related

Apex parse values from Json string into custom field

Im trying to parse trough json string and return values into custom meta data type field on Case. I have created the custom metadata type already, example:
<label>Jordfeil.Casefield</label>
<protected>false</protected>
<values>
<field>CaseField__c</field>
<value xsi:type="xsd:string">Casefield</value>
</values>
<values>
<field>Context__c</field>
<value xsi:type="xsd:string">JORDFEIL</value>
</values>
<values>
<field>JsonField__c</field>
<value xsi:type="xsd:string">key1</value>
</values>
</CustomMetadata>
Json String :
{
"operation": "get-id",
"payload": {
"ApplicationContext": {
"Context": "JORDFEIL",
"SenderId": "xxx",
"subContext": ""
},
"supportIssue": {
"InfoFields": [
{
"Key": "key1",
"Value": "value1"
},
{
"Key": "key2",
"Value": "value2"
},
{
"Key": "key3",
"Value": "value3"
}
],
}
},
"type": "~:CustomerInquiry",
}
My Wrapper class:
public class WebskjemaModel {
public class ApplicationContext {
public String Context;
public String SenderId;
public String subContext;
}
public String operation;
public Payload payload;
public String type;
public class InfoFields {
public String Key;
public String Value;
}
public class Payload {
public ApplicationContext ApplicationContext;
public SupportIssue supportIssue;
}
public class SupportIssue {
public List<InfoFields> InfoFields;
public String assignId;
public String businessObjectType;
public String comment;
public String contactDate;
public String contactName;
public String customerName;
public String customerNo;
public String docClass;
public String format;
public String objectDescription;
public String objectId;
public String subject;
}
public static WebskjemaModel parse(String json) {
return (WebskjemaModel) System.JSON.deserialize(json, WebskjemaModel.class);
}
}
The json string is stored a field in Case called WebskjemaBlob__c.
Below is my class:
public with sharing class WebskjemaCaseCreator {
Map<String, Case> Webskjemastring = new Map<String, Case>();
private Map<Id, Case> cases { get; set; }
private Map<Id, WebskjemaModel> webskjemaModels = new Map<Id, WebskjemaModel>();
public WebskjemaCaseCreator(Map<Id, Case> cases) {
this.cases = cases;
deserializeJson();
mapFields();
}
private void deserializeJson() {
for (Id caseId : cases.keySet()) {
Case c = cases.get(caseId);
WebskjemaModel model = WebskjemaModel.parse(c.Webskjemablob__c);
webskjemaModels.put(caseId, model);
}
}
private void mapFields() {
for (Id caseId : cases.keySet()) {
Case c = cases.get(caseId);
WebskjemaModel model = webskjemaModels.get(caseId);
}
}
private void mapFieldsForSingleCase(Case c, WebskjemaModel model) {
// TODO: Find WebskjemaModel inforfields and loop
// for (list of infofields for this model only){
// TODO: call method mapInfoField
// }
}
private void mapInfoField(Case c, String context, String infoFieldKey, String infoFieldValue) {
// TODO: Find Case field from custom meta data type mapping, based on context and infoFielfKey
// TODO: Put value from inforFieldValue into Case field
}
}
My question is how you get the key value pair from infofields and update the case fields based on the custom meta data type mapping?

cant get my custom exception message from Exception Controller

Why i recived "Unable to find com.example.Api_Angular.Dao.Person with id 4" instead of "Person with this id does not exist".
#GetMapping("/persons/{id}")
public Person getPerson(#PathVariable int id)
{
Person person = personService.getPersonById(id);
System.out.println(person);
if(!(person instanceof Person))
{
throw new NoPersonException("Person with this id does not exist");
}
return person;
}
What im getting:
{
"status": 400,
"msg": "Unable to find com.example.Api_Angular.Dao.Person with id 4",
"request": "uri=/myApp/persons/4"
}
What it should be:
{
"status": 400,
"msg": "Person with this id does not exist",
"request": "uri=/myApp/persons/4"
}
My custom exception:
public class NoPersonException extends RuntimeException {
String msg;
public String getMsg() {
return msg;
}
public NoPersonException() {
}
public NoPersonException(String message) {
this.msg = message;
}
}
Error response:
#Data
#AllArgsConstructor
public class ErrorDetails {
private int status;
private String msg;
private String request;
public ErrorDetails() {
}
}
Exception controller:
#RestControllerAdvice
public class CustomizedResponseEntityExceptionHandler{
#ExceptionHandler
public ResponseEntity<ErrorDetails> response(NoPersonException e,WebRequest webRequest)
{
ErrorDetails customerErrorResponse=new ErrorDetails(HttpStatus.NOT_FOUND.value(),e.getMessage(),webRequest.getDescription(false));
return new ResponseEntity<>(customerErrorResponse,HttpStatus.NOT_FOUND);
}
#ExceptionHandler
public ResponseEntity<ErrorDetails> generalresponse(Exception e,WebRequest webRequest)
{
ErrorDetails customerErrorResponse=new ErrorDetails(HttpStatus.BAD_REQUEST.value(),e.getMessage(),webRequest.getDescription(false));
return new ResponseEntity<>(customerErrorResponse,HttpStatus.BAD_REQUEST);
}
Is there problem with type of exception which is called?
Have you any solution how to solve this problem?
Im using spring boot 2.2.
It sure looks like you are getting an EntityNotFoundException when you call personService.getPersonById(id). You should try wrapping that in a try/catch block to learn more.

can't serialize class org.springframework.data.mongodb.core.geo.GeoJsonPoint

I am using MongoDB Near to find nearest points, getMills is method to find nearest points based on LatLng. It runs fine till fetching data and mapping. while mapping it returns following error. My question is that how can i serialize class to GeoJsonPoint???
It's returning this error : can't serialize class org.springframework.data.mongodb.core.geo.GeoJsonPoint.
private List<Mill> getMills(GetNearByMillsRequest getNearByMillsRequest, BasicResponse basicResponse) {
// get farmerPosition
LatLng fieldPosition = getNearByMillsRequest.getFieldPosition();
double Longitude = fieldPosition.getLongitude();
double Latitude = fieldPosition.getLatitude();
System.out.println("Longitude : " + Longitude + "Latitude : "+ Latitude);
// creating GeoJson Object for farmer field location...
Point point = new Point(Latitude,Longitude);
GeoJsonPoint farmerFieldPosition = new GeoJsonPoint(point);
Float distance = getNearByMillsRequest.getDistance();
// find crop by crop Name
Query query = new Query();
query.addCriteria(Criteria.where(Mill.Constants.MILL_CROP_REQUIRED).elemMatch(Criteria.where(MillCropRequired.Constants.CROP).is(getNearByMillsRequest.getCrop())));
query.addCriteria(Criteria.where(Mill.Constants.MILL_LOCATION).near(farmerFieldPosition));
// TODO Get radius from UserPreferences
NearQuery nearQuery = NearQuery.near(farmerFieldPosition).maxDistance(new Distance(distance, Metrics.KILOMETERS));
nearQuery.query(query);
nearQuery.spherical(true); // if using 2dsphere index, otherwise delete or set false
nearQuery.num(10);
// Problem occurs here, in mapping part...
GeoResults<Mill> results = millDAO.getMongoOperations().geoNear(nearQuery, Mill.class);
System.out.println("results : " + results.getContent().size());
if (results.getContent().isEmpty()) {
basicResponse.setErrorCode(ErrorCode.FORBIDDEN_ERROR);
basicResponse.setResponse("No Crop Found for this region.");
return null;
}
List<GeoResult<Mill>> geoResults = results.getContent();
// add nearBy Mills and return this list...
List<Mill> nearByMills = new ArrayList<>();
for (GeoResult<Mill> result : geoResults) {
Mill mill = result.getContent();
nearByMills.add(mill);
}
return nearByMills;
}
This is my Mill Entity.
public class Mill extends AbstractEntity {
private String vendorId;
private String name;
private String emailAddress;
private String countryCode;
private String phoneNumber;
private String postalAddress;
#GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)
private GeoJsonPoint millLocation;
private String ginningCapacity;
private String storageCapacity;
private ArrayList<MillCropRequired> millCropRequired;
public String getVendorId() {
return vendorId;
}
public void setVendorId(String vendorId) {
this.vendorId = vendorId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getPostalAddress() {
return postalAddress;
}
public void setPostalAddress(String postalAddress) {
this.postalAddress = postalAddress;
}
public GeoJsonPoint getMillLocation() {
return millLocation;
}
public void setMillLocation(GeoJsonPoint millLocation) {
this.millLocation = millLocation;
}
public String getGinningCapacity() {
return ginningCapacity;
}
public void setGinningCapacity(String ginningCapacity) {
this.ginningCapacity = ginningCapacity;
}
public ArrayList<MillCropRequired> getCropRequirnment() {
return millCropRequired;
}
public void setCropRequirnment(ArrayList<MillCropRequired> millCropRequired) {
this.millCropRequired = millCropRequired;
}
public String getStorageCapacity() {
return storageCapacity;
}
public void setStorageCapacity(String storageCapacity) {
this.storageCapacity = storageCapacity;
}
public static class Constants extends AbstractEntity.Constants {
public static final String PHONE_NUMBER = "phoneNumber";
public static final String VENDOR_ID = "vendorId";
public static final String MILL_CROP_REQUIRED = "millCropRequired";
public static final String MILL_LOCATION = "millLocation";
}
}

Error in annoting camel swagger Rest Service

I have created a rest Service using Apache Camel Swagger component. The rest service works fine but the request and response schema is not what i intended of.
The schema that i am trying to create is :
{
"GetStudentData": [
{
"RollNumber": "1",
"Name": "ABC",
"ClassName": "VII",
"Grade": "A"
}]
}
For this i have created a model as:
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement(name = "GetStudentData")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name="", propOrder={"studentInfo"})
public class StudentInfoWrapper {
#XmlElement(required=true)
private List<Student> studentInfo;
private double visiteddate;
public double getVisiteddate() {
return visiteddate;
}
public void setVisiteddate(double visiteddate) {
this.visiteddate = visiteddate;
}
public List<Student> getStudentInfo() {
return studentInfo;
}
public void setStudentInfo(List<Student> studentInfo) {
studentInfo = studentInfo;
}
}
And my student class is:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name="", propOrder={"RollNumber", "Name", "ClassName", "Grade"})
public class Student {
private String RollNumber;
private String Name;
private String ClassName;
private String Grade;
public String getRollNumber() {
return RollNumber;
}
public void setRollNumber(String rollNumber) {
RollNumber = rollNumber;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getClassName() {
return ClassName;
}
public void setClassName(String className) {
ClassName = className;
}
public String getGrade() {
return Grade;
}
public void setGrade(String grade) {
Grade = grade;
}
}
So when i load the above service into the swaggerUI it doesn't show the schema that i want.
How can i i get the desired schema. Looking forward to your answers.
Thanks in advance.

In Spring-mvc the attribute names in view have to always match the property names in model?

In the http request body, the way password string is passed is "pass=1111", however in the bean the way password is defined is ''private String password". Is there a way I can use annotation to handle the difference or I have to always match names?
The Http request is like this
curl -H "Accept:text/html" -H "Content-Type application/x-www-form-urlencoded" -d 'email=test%40gmail.com&pass=1111&passconfirm=1111&name=x+y' "http://localhost:8080/project/register"
Handler method is
#RequestMapping(method = RequestMethod.POST, headers = "content-type=application/x-www-form-urlencoded")
public String register(#ModelAttribute UserAccountBean account) ...
UserAccountBean is
public class UserAccountBean2 {
#NotNull
#Size(min = 1, max = 25)
private String name;
#NotNull
#Size(min = 4, max = 8)
private String password;
#NotNull
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public String toString() {
return new ToStringCreator(this).append("name", name).append("password", password).toString();
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Use #RequestParam annotation in #InitBinder annotated method, and set the desired value manually.
UserController
#InitBinder(value="user")
public void bind(WebDataBinder dataBinder, WebRequest webRequest, #RequestParam(value="pass", required=false) String password) {
User user = (User) dataBinder.getTarget();
user.setPassword(password);
}
Is there a way I can use annotation to
handle the difference or I have to
always match names?
AFAIK there is no ready-made annotation in Spring MVC that can resolve your problem; you need custom setup to handle the situation.
WebModelAttribute
#Target({ElementType.METHOD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface WebModelAttribute {
String modelAttributeName();
WebParameterMapping[] parameterMappings();
}
WebParameterMapping
#Target({ElementType.METHOD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface WebParameterMapping {
String webProperty();
String beanProperty();
}
UserController
#Controller
public class UserController extends AbstractController {
#Override
#InitBinder(value="user")
#WebModelAttribute(modelAttributeName="user", parameterMappings={#WebParameterMapping(webProperty="pass", beanProperty="password")})
protected void bindWebParameters(WebDataBinder dataBinder, WebRequest webRequest, WebParameterResolver mappingResolver) {
super.bindWebParameters(dataBinder, webRequest, mappingResolver);
}
AbstractController
public class AbstractController {
protected void bindWebParameters(WebDataBinder dataBinder, WebRequest webRequest, WebParameterResolver mappingResolver) {
if(mappingResolver != null && dataBinder.getTarget() != null && dataBinder.getObjectName().equals(mappingResolver.getModelAttributeName())) {
String[] allowedFields = mappingResolver.getAllowedFields(dataBinder.getAllowedFields());
String[] disallowedFields = mappingResolver.getDisallowedFields(dataBinder.getDisallowedFields());
dataBinder.setAllowedFields(allowedFields);
dataBinder.setDisallowedFields(disallowedFields);
dataBinder.bind(mappingResolver.getPropertyValues(dataBinder, webRequest));
}
}
}
WebParameterResolver
public class WebParameterResolver {
private String modelAttributeName;
private WebParameterMapping[] parameterMappings;
public WebParameterResolver(String modelAttributeName,
WebParameterMapping[] parameterMappings) {
this.modelAttributeName = modelAttributeName;
this.parameterMappings = parameterMappings;
}
public String getModelAttributeName() {
return modelAttributeName;
}
public String[] getDisallowedFields(String[] existingDisallowedFields) {
List<String> disallowedFields = new ArrayList<String>();
for (WebParameterMapping parameterMapping : parameterMappings) {
disallowedFields.add(parameterMapping.webProperty());
}
if (existingDisallowedFields != null) {
for (String disallowedField : existingDisallowedFields) {
disallowedFields.add(disallowedField);
}
}
return disallowedFields.toArray(new String[disallowedFields.size()]);
}
public String[] getAllowedFields(String[] existingAllowedFields) {
List<String> allowedFields = new ArrayList<String>();
for (WebParameterMapping parameterMapping : parameterMappings) {
allowedFields.add(parameterMapping.beanProperty());
}
if (existingAllowedFields != null) {
for (String allowedField : existingAllowedFields) {
allowedFields.add(allowedField);
}
}
return allowedFields.toArray(new String[allowedFields.size()]);
}
public MutablePropertyValues getPropertyValues(WebDataBinder dataBinder,
WebRequest webRequest) {
MutablePropertyValues propertyValues = new MutablePropertyValues();
for (WebParameterMapping parameterMapping : parameterMappings) {
String[] values = webRequest.getParameterValues(parameterMapping.webProperty());
if (values == null || values.length == 0) {
// do nothing
} else if (values.length == 1) {
propertyValues.add(parameterMapping.beanProperty(), values[0]);
} else {
propertyValues.add(parameterMapping.beanProperty(), values);
}
}
dataBinder.bind(propertyValues);
return propertyValues;
}
}
CustomArgumentResolver
public class CustomArgumentResolver implements WebArgumentResolver {
#Override
public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception {
if(methodParameter.getParameterType().equals(WebParameterResolver.class)) {
WebModelAttribute webModelAttribute = methodParameter.getMethod().getAnnotation(WebModelAttribute.class);
if(webModelAttribute == null) {
throw new RuntimeException("method must have WebModelAttribute");
}
return new WebParameterResolver(webModelAttribute.modelAttributeName(), webModelAttribute.parameterMappings());
}
return UNRESOLVED;
}
}
beans.xml
<bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="customArgumentResolvers" ref="timetracking.annotations.CustomArgumentResolver"/>
</bean>
<bean name="timetracking.annotations.CustomArgumentResolver"
class="timetracking.annotations.CustomArgumentResolver" />
You can also have a public static void bindWebParameters(...) method in some helper class; so you don't have to extend the AbstractController every time.
You can achieve it with this:
#RequestMapping(method = RequestMethod.POST, headers = "content-type=application/x-www-form-urlencoded")
public String register(#ModelAttribute("userAccountBean") UserAccountBean account) ...
#ModelAttribute("userAccountBean")
public UserAccountBean getUserAccountBean(HttpServletRequest req) {
UserAccountBean uab = new UserAccountBean();
uab.setPassword(req.getParameter("pass"));
return uab;
}
There is no annotation based solution in 3.0.
Just provide additional getPass() setPass(String pass) method and you should be set.