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?
Related
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);
I have a problem with annotations and generating documentation (spring-boot, springfox-swagger2).
What should be a proper annotation for String Collection in my TechnologyDTO?
Does swagger support java.util.Collection?
After generation I have:
Model schema:
{
"technologies": {},
"userGUID": "string"
}
Model:
TechnologyDTO {
technologies (Collection«string»): User main technology names,
userGUID (string): User guid
}
Collection«string» {
}
My TechnologyDTO:
public class TechnologyDTO {
private final String userGUID;
private final Collection<String> technologies;
public TechnologyDTO(String userGUID, Collection<String> technologies) {
this.userGUID = userGUID;
this.technologies = technologies;
}
#ApiModelProperty(notes = "User guid", dataType="string", required = true)
public String getUserGUID() {
return userGUID;
}
#ApiModelProperty(notes = "User main technology names", required = true)
public Collection<String> getTechnologies() {
return technologies;
}
public static TechnologyDTO createEmptyDTO() {
return new TechnologyDTO(null, null);
}
}
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.
I created a class like this :
namespace Web.Models
{
public class TimeLineStep
{
public string Code { get; set; }
public string Title;
public string Description;
public string Url;
public string Status;
public string Category;
}
}
And I use this class in a model like this :
namespace Web.Models
{
public class TimelineList
{
private List<TimelineStep> _steps;
public List<TimelineStep> Steps
{
get
{
return this._steps ?? (this._steps = new List<TimelineStep>());
}
set
{
this._steps = value;
}
}
Both are under same namespace but the class TimeLineStep is underlined with red and it says cannot resolve symbol 'TimeLineStep'
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.