I'm getting the below error while trying to post a request through Swagger. This works fine when I do it through Postman.
I'm using springBootVersion = '2.1.0.RELEASE', swaggerVersion = '2.9.2'.
This is my Json mapping class.
DataExport.java
#XmlRootElement(name = "DataExport")
public class DataExport
{
#JsonProperty("engineName")
private String engineName;
#JsonProperty("searchQuery")
private String searchQuery;
public DataExport()
{
super();
}
public DataExport(String aEngineName, String aSearchQuery)
{
super();
this.engineName = aEngineName;
this.searchQuery = aSearchQuery;
}
RestController.java
#RequestMapping(value = "/export", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
#ApiOperation(authorizations = {
#Authorization(value = "oauth") }, value = "Initiates the job for export", response = String.class)
#ApiResponses({ #ApiResponse(code = 200, message = "Request accepted."),
#ApiResponse(code = 500, message = "The export could not be started due to an internal server error.") })
public String getJobId(#RequestBody DataExport aDataExport, HttpServletResponse aResponse,
#Value("${com.xyz.dataexportmodule.defaultfilelocation}") final String aLocation)
throws Exception
{
LOG.info("Initializing export for Engine {} and Query {}", aDataExport.getEngineName(),
aDataExport.getSearchQuery());
String exportLocation = aLocation
....
I want to pass this JSON
{
"engineName":"ABC",
"searchQuery":"*"
}
But I'm getting this error:
2019-04-01 13:42:05,022 [https-jsse-nio-8443-exec-19] WARN o.s.w.s.m.s.DefaultHandlerExceptionResolver - Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct an instance of `com.xyz.dataexportmodule.persistence.entity.DataExport` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('string');
nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct an instance of `com.xyz.dataexportmodule.persistence.entity.DataExport` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('string')
at [Source: (PushbackInputStream); line: 1, column: 1]]
I'm unable to figure out what is the issue, somebody please help.
P.s: My Swagger Screenshot
enter image description here
Solution: After making these changes in DataExport class and removing Location variable in REST controller method, this issue got resolved.
#JsonIgnoreProperties(ignoreUnknown = true)
#ApiModel(value = "Data Export", description = "A JSON object corresponding to Data Export entity.")
public class DataExport
{
private String mEngineName;
private String mSearchQuery;
/**
* Default CTor.
*/
public DataExport()
{
}
#JsonCreator
public DataExport(#JsonProperty("engineName") String aEngineName,
#JsonProperty("searchQuery") String aSearchQuery)
{
mEngineName = aEngineName;
mSearchQuery = aSearchQuery;
}
Your screenshot show that you have two body, which can't be done.
I don't know what you're trying to do, but just for test remove the field aLocation, and it will be OK.
Related
I'm trying to parse following JSON string with one field inside. Unfortunatelly still getting exception:
InvalidTypeIdException: Could not resolve type id 'pin' as a subtype of `com.example.dto.AuthorizationRequest`: known type ids = [AuthorizationRequest]
Here is base class:
#JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT ,use = JsonTypeInfo.Id.NAME)
#JsonSubTypes({
#JsonSubTypes.Type(value = AuthorizationRequest.class, name = "AuthorizationRequest")})
public interface IMessage {}
Derived class:
public class AuthorizationRequest implements IMessage {
#JsonProperty( value = "pin", required = true )
private String pin;
public String getPin() {
return pin;
}
public void setPin(String pin) {
this.pin = pin;
}
}
Test:
#Test
void test(){
String request =
"{\n"
+ " \"AuthorizationRequest\": {\n"
+ " \"pin\": \"1234\"\n"
+ " }\n"
+ "}";
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
AuthorizationRequest authorizationRequest = null;
try {
authorizationRequest = objectMapper.readValue(request, AuthorizationRequest.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
What am I missing? I need to use request name as json root element.
You can delete objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true) line. The error is due to the fact that the line unwraps the initial object so escalating of one level at the intern of the json file obtaining the {"pin" : "1234"} json with the ""AuthorizationRequest" label. The JsonTypeInfo.As.WRAPPER_OBJECT annotation indicates to the jackson library to escalate again of one level the json file so obtaining the "1234" string labelled by "pin".The JsonTypeInfo.Id.NAME compares the "pin" string with the names of subclasses and because of it fails causing the issue and the error message.
I have a problem with deserialization implemented in AWS Lex Lambda hook. I have an AWS Lambda function to validate the user input, but I keep getting JSONMapping errors.
The Lex json is like this:
{
"currentIntent": {
"name": "intent-name",
"slots": {
"slot-name": "value",
"slot-name": "value",
"slot-name": "value"
},
"confirmationStatus": "None, Confirmed, or Denied (intent confirmation, if configured)",
},
"bot": {
"name": "bot-name",
"alias": "bot-alias",
"version": "bot-version"
},
"userId": "User ID specified in the POST request to Amazon Lex.",
"inputTranscript": "Text used to process the request",
"invocationSource": "FulfillmentCodeHook or DialogCodeHook",
"outputDialogMode": "Text or Voice, based on ContentType request header in runtime API request",
"messageVersion": "1.0",
"sessionAttributes": {
"key1": "value1",
"key2": "value2"
}
}
And my Java bean for deserializing this JSON is:
public class RequestInput {
public class CurrentIntent {
#JsonProperty("name")
String name;
#JsonProperty("slots")
Map<String, String> slots;
#JsonProperty("confirmationStatus")
String confirmationStatus;
public CurrentIntent(#JsonProperty("name") String name, #JsonProperty("slots") Map<String, String> slots, #JsonProperty("confirmationStatus") String confirmationStatus) {
this.name = name;
this.slots = slots;
this.confirmationStatus = confirmationStatus;
}
}
#JsonProperty("currentIntent")
CurrentIntent currentIntent;
#JsonProperty("bot")
Map<String, String> bot;
#JsonProperty("userId")
String userId;
#JsonProperty("inputTranscript")
String inputTranscript;
#JsonProperty("invocationSource")
String invocationSource;
#JsonProperty("outputDialogMode")
String outputDialogMode;
#JsonProperty("messageVersion")
String messageVersion;
#JsonProperty("sessionAttributes")
Map<String, String> sessionAttributes;
#JsonCreator
public RequestInput(#JsonProperty("currentIntent") CurrentIntent currentIntent, #JsonProperty("bot") Map<String, String> bot,
#JsonProperty("userId") String userId, #JsonProperty("inputTranscript") String inputTranscript,
#JsonProperty("invocationSource") String invocationSource, #JsonProperty("outputDialogMode") String outputDialogMode,
#JsonProperty("messageVersion") String messageVersion, #JsonProperty("sessionAttributes") Map<String, String> sessionAttributes) {
this.currentIntent = currentIntent;
this.bot = bot;
this.userId = userId;
this.inputTranscript = inputTranscript;
this.invocationSource = invocationSource;
this.outputDialogMode = outputDialogMode;
this.messageVersion = messageVersion;
this.sessionAttributes = sessionAttributes;
}
#Override
public String toString() {
return "Intent " + currentIntent.toString() + "; Bot " + bot.toString() + "; InputTranscript " + inputTranscript;
}
}
In the handler class I just try to invoke RequestInput.toString() method, but I keep getting this error:
An error occurred during JSON parsing: java.lang.RuntimeException
java.lang.RuntimeException: An error occurred during JSON parsing
Caused by: java.io.UncheckedIOException: com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class com.comelit.lex.LexIntercomCallValidate$RequestInput]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
at [Source: lambdainternal.util.NativeMemoryAsInputStream#55d56113; line: 1, column: 2]
Caused by: com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class com.comelit.lex.LexIntercomCallValidate$RequestInput]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
at [Source: lambdainternal.util.NativeMemoryAsInputStream#55d56113; line: 1, column: 2]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1106)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:296)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:133)
at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1511)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1102)
Add a default constructor to class RequestInput. This error generally indicates the inability to instantiate the class to be mapped with the received JSON:
public RequestInput() {}
I'm using Spring, following is my controller code:
#RequestMapping(value = "/campaigns/addTask", method = RequestMethod.POST)
public Campaign addTaskToCampaign(#RequestParam(value = "campaignName")String campaignName,#Valid #RequestBody Task task) {
Campaign campaign = campaignInterface.findByName(campaignName);
if (campaign!=null){
List<String> task_ids;
if (campaign.getTask_ids()==null){
task_ids = new ArrayList<>();
}else{
task_ids= campaign.getTask_ids();
}
Task newTask = taskInterface.save(task);
task_ids.add(newTask.getId());
campaign.setTask_ids(task_ids);
return campaignInterface.save(campaign);
}
return null;
}
Where my task model is:
#Document(collection = "tasks")
public class Task {
#Id
private String id;
private String name;
private int points;
private List<Question>questions;
private List<String>answers;
...
}
And the nested question model is:
public class Question {
private Boolean isText = false;
private String questionText;
}
But, the same model when POSTing as nested json throws an HTTP 400 exception saying json unreadable exception, and it tried to parse the String questionText field as a boolean value.
Here is what im POSTing:
{
"name" : "Test Task 3",
"questions": [{ "questionText":"What is the name you college festival?","isText":true}]
}
And the exception that comes is this:
{
"timestamp": 1497508476467,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.http.converter.HttpMessageNotReadableException",
"message": "JSON parse error: Can not deserialize value of type boolean from String \"What is the name you college festival?\": only \"true\" or \"false\" recognized; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type boolean from String \"What is the name you college festival?\": only \"true\" or \"false\" recognized\n at [Source: java.io.PushbackInputStream#6a371c03; line: 3, column: 32] (through reference chain: com.frapp.CBM.prod.model.Task[\"questions\"]->java.util.ArrayList[0]->com.frapp.CBM.prod.model.Question[\"questionText\"])",
"path": "/campaigns/addTask"
}
Any help is appreciated. ive been trying for hours. Thank you, in advance.
The first comment I would make to any of my developers is: please can you rename your boolean. This is because the getter will look like: isIsText().
As a general rule it is good practice to avoid starting a field name with "get", "set", or "is".
This is because these are the prefixes of java-beans properties.
#RequestMapping(value = "/campaigns/addTask", method = RequestMethod.POST)
public Campaign addTaskToCampaign(#RequestParam Map<String,Object> campaignName,#Valid #RequestBody Task task){
/*
if there is exception then just remove Task put it into json to catch into map.
access just as map by campaignName.get("key_name");
*/
Campaign campaign = campaignInterface.findByName(campaignName);
if (campaign!=null){
List<String> task_ids;
if (campaign.getTask_ids()==null){
task_ids = new ArrayList<>();
}else{
task_ids= campaign.getTask_ids();
}
Task newTask = taskInterface.save(task);
task_ids.add(newTask.getId());
campaign.setTask_ids(task_ids);
return campaignInterface.save(campaign);
}
return null;
}
Is there any simple methods to return exception in JSON using Rest api?
I've already googled this question, but all solutions i see, was about throwing exceptions during some calculations. But what if income parameters are wrong? I mean what if there is sone string instead of int input parameter?
I created some DTO class for input data:
#XmlRootElement
public class RequestDTO implements Serializable{
private static final long serialVersionUID = 1L;
#XmlElement(name = "request_id")
private String requestId;
#XmlElement(name = "site")
private List<String> sitesIds;
#XmlElement(name = "date_begin")
#JsonSerialize(using = DateSerializer.class)
#JsonDeserialize(using = DateDeserializer.class)
private Date dateBegin;
#XmlElement(name = "date_end")
#JsonSerialize(using = JsonDateSerializer.class)
#JsonDeserialize(using = JsonDateDeserializer.class)
private Date dateEnd;
#XmlElement(name = "volume")
private double volume;
// there is getters and setters
}
If i sent something like 'qwerty' instead of 'volume' field in my json request i'l see error message like Runtime. Is it possible to handle it in someway? I mean to return error in json with such structure?
public class ExceptionDTO {
private String shortExceptionMessage;
private String stackTrace;
public ExceptionDTO(String shotExceptionMessage, String stackTrace){
this.shortExceptionMessage = shotExceptionMessage;
this.stackTrace = stackTrace;
}
public String getShortExceptionMessage() {
return shortExceptionMessage;
}
public String getStackTrace() {
return stackTrace;
}
}
UPD1:
#Provider
#Singleton
public class ExceptionMapperProvider implements ExceptionMapper<Exception>{
#Override
public Response toResponse(final Exception e) {
StringBuilder trace = new StringBuilder();
IntStream.range(0, e.getStackTrace().length)
.forEach(i -> trace.append(e.getStackTrace()[i]).append('\n'));
ExceptionDTO exceptionMessage = new ExceptionDTO(
e.toString(),
trace.toString()
);
return Response.status(500).entity(exceptionMessage).build();
}
}
As it's not really clear if you are interested on checking if field or value of the payload is correct, here are a few ways to work with both.
If you want to check if the value for a field is correct (ie volume field value should be greater than zero etc), check out bean validation. This makes use of annotations on the fields you want to verify.
// for example
#Min(value = 0, message = "invalid message")
private double range;
To use your ExceptionDTO as error response whenever one of those validation fails, you can do so by creating an ExceptionMapper<ConstraintViolationException>. check it here for more details.
If you are checking for the invalid field (ie client sends ragne fields instead of range), have a look at the stack trace on what exception is being thrown. Then register an exception mapper with your ExceptionDTO as body.
For example, if UnrecognizedPropertyException is thrown then you can add:
#Provider
public class UnrecognizedPropertyExceptionMapper implements ExceptionMapper<UnrecognizedPropertyException> {
#Override
public Response toResponse(UnrecognizedPropertyException e) {
ExceptionDTO myDTO = // build response
return Response.status(BAD_REQUEST).entity(myDTO).build();
}
}
If you want to validate input parameters in the request, you should return status code 400 (Bad Request) along with the error details. You can simply send json
{ "error": { "message": "string received for parameter x, where as int expected" } with the response status code 400.
`
I did a bit of research and determined that the best way to encode a Java exception in JSON is to use a convention developed by Oasis that looks like this:
{
"error": {
"code": "400",
"message": "main error message here",
"target": "approx what the error came from",
"details": [
{
"code": "23-098a",
"message": "Disk drive has frozen up again. It needs to be replaced",
"target": "not sure what the target is"
}
],
"innererror": {
"trace": [ ... ],
"context": [ ... ]
}
}
}
details is a list that should have an entry for each nested cause exception in the chain.
innererror.trace should include the stack trace if you wish, as a list of string values.
The response status code should be 400 unless you have a good reason for making it something else, and the code in the structure should match whatever you sent.
Write one method to convert a Java exception to this format, and you are done. Use it consistently and your JS code will be able to handle and display the exception values.
More of the details of the other approaches evaluated and dismissed are covered in this blog post on JSON REST API – Exception Handling
https://agiletribe.purplehillsbooks.com/2015/09/16/json-rest-api-exception-handling/
Here is the java method to convert an exception to this format:
public static JSONObject convertToJSON(Exception e, String context) throws Exception {
JSONObject responseBody = new JSONObject();
JSONObject errorTag = new JSONObject();
responseBody.put("error", errorTag);
errorTag.put("code", 400);
errorTag.put("target", context);
JSONArray detailList = new JSONArray();
errorTag.put("details", detailList);
String lastMessage = "";
Throwable runner = e;
while (runner!=null) {
String className = runner.getClass().getName();
String msg = runner.toString();
runner = runner.getCause();
JSONObject detailObj = new JSONObject();
detailObj.put("message",msg);
int dotPos = className.lastIndexOf(".");
if (dotPos>0) {
className = className.substring(dotPos+1);
}
detailObj.put("code",className);
System.out.println(" ERR: "+msg);
detailList.put(detailObj);
}
JSONObject innerError = new JSONObject();
errorTag.put("innerError", innerError);
JSONArray stackList = new JSONArray();
runner = e;
while (runner != null) {
for (StackTraceElement ste : runner.getStackTrace()) {
String line = ste.getFileName() + ":" + ste.getMethodName() + ":" + ste.getLineNumber();
stackList.put(line);
}
stackList.put("----------------");
runner = runner.getCause();
}
errorTag.put("stack", stackList);
return responseBody;
}
I have a rest endpoint which returns List<VariablePresentation>. I am trying to test this rest endpoint as
#Test
public void testGetAllVariablesWithoutQueryParamPass() throws Exception {
final ClientRequest clientCreateRequest = new ClientRequest("http://localhost:9090/variables");
final MultivaluedMap<String, String> formParameters = clientCreateRequest.getFormParameters();
final String name = "testGetAllVariablesWithoutQueryParamPass";
formParameters.putSingle("name", name);
formParameters.putSingle("type", "String");
formParameters.putSingle("units", "units");
formParameters.putSingle("description", "description");
formParameters.putSingle("core", "true");
final GenericType<List<VariablePresentation>> typeToken = new GenericType<List<VariablePresentation>>() {
};
final ClientResponse<List<VariablePresentation>> clientCreateResponse = clientCreateRequest.post(typeToken);
assertEquals(201, clientCreateResponse.getStatus());
final List<VariablePresentation> variables = clientCreateResponse.getEntity();
assertNotNull(variables);
assertEquals(1, variables.size());
}
This test fails with error saying
org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token(..)
How can I fix this issue?
That looks like a Jackson error, where it's expecting to parse an array (which would begin with a '[') but it's encountering the beginning token for an object ('{'). From looking at your code, Im guessing it's trying deserialise JSON into your List but it's getting the JSON for an object.
What does the JSON your REST endpoint returns look like? It ought to look like this
[
{
// JSON for VariablePresentation value 0
"field0": <some-value>
<etc...>
},
<etc...>
]