How to show conversion error along with other validation errors when creating REST API?
- Spring Boot
- Spring MVC
- REST Controller
@Data
@AllArgsConstructor
class DTO
{
@NotNull
@Min(1)
private Integer id;
}
@RestController
class ExampleController
{
@PostMapping("/")
public void createEndpoint(@Valid @RequestBody DTO dto) {
return "{"message": "OK"}";
}
}
When I make request to this endpoint,
POST /
{
"id: "abrakadabra"
}
I would like to get something like this
{
"errors": {
"id": [
{ code: "invalid_format", message: "Field must contain only digits" }
]
}
}
What I actually get?
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `int` from String "abrakadabra": not a valid `int` value;
nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `int` from String "abrakadabra": not a valid `int` value
at [Source: (PushbackInputStream); line: 2, column: 24] (through reference chain: com.mylid.back.dtos.CreateCompanyDto["pricing_plan_id"])]
I know that I can create custom annotation for validation.
BUT the problem is that the process does not reach validation. It fails on deserialization step.
[Possible way]
There is one dirty trick, to change Integer type in DTO to String. Create custom annotation to check if String contains only digits. Then manually map this String from DTO to Integer in Entity. Then save to database.
What are other ways how to solve this problem?
It is very weird, that there is a few topics on Google and StackOverFlow for this particular problem. And on almost every page the accepted answer is «it is not our problem, API clients should pass integer».
In PHP we can easily do it with ‘integer’ validator, almost every framework has it, and it will not prevent other fields from validation if I pass «abracadabra» instead of «1».
2021-08-02
This Stackoverflow Topic
comes up with two questions.
- Q1: how to distinguish exceptions between
- data binding when conversion http request body to object
- customized validation defined as annotation on that object
- Q2: how to display valuable information when data binding fails
All the code can be found in this repo. All test code related to
this topic is under package com.example.demo.validation
.
The main data structure to be used:
1 |
@Data |
The project can be run with command:
gradlew clean bootRun
1 Distinguish Exceptions
It is possible to achieve this by ControllerAdvice
. The most important thing is to find the concise exception class
thrown, and in our case, it is org.springframework.http.converter.HttpMessageNotReadableException
.
1 |
package com.example.demo.validation; |
2 Display Valuable Information for Data Binding Exception
The text got from HttpMessageNotReadableException
is created by the spring framework and is kinda robotic. We can use
customize json deserializer to make the message more readable. Jackson itself doesn’t support
customized information to be thrown in data binding fail yet.
Add a field to Person
:
1 |
@JsonDeserialize(using = MyIntDeserializer.class) |
1 |
class MyIntDeserializer extends JsonDeserializer<Integer> { |
3 Test Step
3.1 Test Validation Fail
Request:
- curl -X POST “http://localhost:8080/validationTest» -H “accept: /“ -H “Content-Type: application/json” -d “{ ”ageInt”: ”0”, ”ageString”: ”1a”}”
Response:
1 |
{ |
3.2 Test Data Binding Fail
Request:
- curl -X POST “http://localhost:8080/validationTest» -H “accept: /“ -H “Content-Type: application/json” -d “{ ”ageInt”: ”1a”, ”ageString”: ”0”}”
Response:
1 |
{ |
3.3 Test Data Binding in Customized Deserializer
Request:
- curl -X POST “http://localhost:8080/validationTest» -H “accept: /“ -H “Content-Type: application/json” -d “{ ”ageInt”: 0, ”ageString”: ”0”, ”ageStringWithCustomizeErrorMessage”: ”aa”}”
Response:
1 |
{ |
Request:
- curl -X POST “http://localhost:8080/validationTest» -H “accept: /“ -H “Content-Type: application/json” -d “{ ”ageInt”: 0, ”ageString”: ”0”, ”ageStringWithCustomizeErrorMessage”: ”-1”}”
Response:
1 |
{ |
It’s still a little robotic, but with more concise information offered by code.
3.4 Tips
Open http://localhost:8080/swagger-ui/#/validation-test-controller/validationTestUsingPOST
in browser and all requests can be made in web page easily.
Today We are Going To Solve JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String in Java. Here we will Discuss All Possible Solutions and How this error Occurs So let’s get started with this Article.
Contents
- 1 How to Fix JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String Error?
- 1.1 Solution 1 : Use the “yyyy-MM-dd’T’HH:mm:ss.SSS” format
- 1.2 Solution 2 : Use the below code
- 2 Conclusion
- 2.1 Also Read These Solutions
- How to Fix JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String Error?
To Fix JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String Error just Use the “yyyy-MM-dd’T’HH:mm:ss.SSS” format. You should use the “yyyy-MM-dd’T’HH:mm:ss.SSS” format to solve this error. So just use it like below
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss[.SSS][.SS][.S]") private LocalDateTime updatedTime;
- JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String
To Fix JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String Error just Use the below code. You can use the below one code to solve this error
@JsonDeserialize(using = LocalDateTimeDeserializer.class) @JsonSerialize(using = LocalDateTimeSerializer.class) private LocalDateTime pickupDate;
Solution 1 : Use the “yyyy-MM-dd’T’HH:mm:ss.SSS” format
You should use the “yyyy-MM-dd’T’HH:mm:ss.SSS” format to solve this error. So just use it like below
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss[.SSS][.SS][.S]")
private LocalDateTime updatedTime;
Solution 2 : Use the below code
You can use the below one code to solve this error
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime pickupDate;
Conclusion
So these were all possible solutions to this error. I hope your error has been solved by this article. In the comments, tell us which solution worked? If you liked our article, please share it on your social media and comment on your suggestions. Thank you.
Also Read These Solutions
- Cannot open local file – Chrome: Not allowed to load local resource
- OpenCV(4.1.2) error: (-215:Assertion failed) !ssize.empty() in function ‘cv::resize’
- OpenSSL Error messages: error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed
- ESLint: Component definition is missing displayName (react/display-name)
- “IsADirectoryError: [Errno 21] Is a directory: It is a file”
Photo by Annie Spratt
Spring Boot projects primarily use the JSON library Jackson to serialize and deserialize objects. It is especially useful that Jackson automatically serializes objects returned from REST APIs and deserializes complex type parameters like @RequestBody
.
In a Spring Boot project the automatically registered MappingJackson2HttpMessageConverter
is usually enough and makes JSON conversions simple, but this may have some issues which need custom configuration. Let’s go over a few good practices for them.
Configuring a Custom Jackson ObjectMapper
In Spring REST projects a custom implementation of MappingJackson2HttpMessageConverter
helps to create the custom ObjectMapper
, as seen below. Whatever custom implementation you need to add to the custom ObjectMapper
can be handled by this custom converter:
public class CustomHttpMessageConverter extends MappingJackson2HttpMessageConverter {
private ObjectMapper initCustomObjectMapper() {
ObjectMapper customObjectMapper = new ObjectMapper();
return customObjectMapper;
}
// ...
}
Additionally, some MappingJackson2HttpMessageConverter
methods, such as writeInternal
, can be useful to override in certain cases. I’ll give a few examples in this article.
In Spring Boot you also need to register a custom MappingJackson2HttpMessageConverter
like below:
@Bean
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
return new CustomHttpMessageConverter();
}
Serialization
Pretty-printing
Pretty-printing in Jackson is disabled by default. By enabling SerializationFeature.INDENT_OUTPUT
in the ObjectMapper
configuration pretty-print output is enabled (as in the example below). Normally a custom ObjectMapper
is not necessary for setting the pretty-print configuration. In some cases, however, like one case of mine in a recent customer project, this configuration might be necessary.
For example, passing a URL parameter can enable pretty-printing. In this case having a custom ObjectMapper
with pretty-print enabled and keeping the default ObjectMapper
of MappingJackson2HttpMessageConverter
as is could be a better option.
public class CustomHttpMessageConverter extends MappingJackson2HttpMessageConverter {
private ObjectMapper initiatePrettyObjectMapper() {
ObjectMapper customObjectMapper = new ObjectMapper();
customObjectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
// additional indentation for arrays
DefaultPrettyPrinter pp = new DefaultPrettyPrinter();
pp.indentArraysWith(new DefaultIndenter());
customObjectMapper.setDefaultPrettyPrinter(pp);
return customObjectMapper;
}
}
Conditionally Filtering the Fields
When serializing a response object you may need to include or ignore one or more fields depending on their values. Let’s assume a model class UserResponse
like below.
Notice that we used @JsonIgnore
which is completely discarding the annotated field from serialization. Conditional filtering is different and it can be done using SimpleBeanPropertyFilter
objects set to the filter provider of the ObjectMapper
objects. Also notice that @JsonFilter
annotation is used for UserResponse
which points to which filter will be used by ObjectMapper
during the serialization.
@JsonFilter("userCodeFilter")
public class UserResponse {
public Integer userId;
public String username;
public Integer code;
@JsonIgnore
public String status;
}
Here we add a filter called userCodeFilter
—like the one we added to the custom ObjectMapper
of CustomHttpMessageConverter
—which will include the UserResponse
class’s code field in the serialization if its value is greater than 0. You can add multiple filters to ObjectMapper
for different models.
public class CustomHttpMessageConverter extends MappingJackson2HttpMessageConverter {
private ObjectMapper initiatePrettyObjectMapper() {
ObjectMapper customObjectMapper = new ObjectMapper();
customObjectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
// additional indentation for arrays
DefaultPrettyPrinter pp = new DefaultPrettyPrinter();
pp.indentArraysWith(new DefaultIndenter());
customObjectMapper.setDefaultPrettyPrinter(pp);
PropertyFilter userCodeFilter = new SimpleBeanPropertyFilter() {
@Override
public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer)
throws Exception {
if (include(writer)) {
if (!writer.getName().equals("code")) {
writer.serializeAsField(pojo, jgen, provider);
return;
}
int intValue = ((UserResponse) pojo).code;
if (intValue > 0) {
writer.serializeAsField(pojo, jgen, provider);
}
} else if (!jgen.canOmitFields()) {
writer.serializeAsOmittedField(pojo, jgen, provider);
}
}
@Override
protected boolean include(BeanPropertyWriter writer) {
return true;
}
@Override
protected boolean include(PropertyWriter writer) {
return true;
}
};
FilterProvider filters = new SimpleFilterProvider().addFilter("userCodeFilter", userCodeFilter);
customObjectMapper.setFilterProvider(filters);
return customObjectMapper;
}
}
Deserialization
JSON String Parse Error Handling in Spring Boot
This one is a little tricky. Deserialization of a JSON @RequestParam
object can cause parsing errors if the JSON object is not well-formed. The errors thrown in Jackson’s deserialization level just before it’s pushed to Spring Boot occur at that level, so Spring Boot doesn’t catch these errors.
Deserialization of Jackson maps JSON to POJOs and finally returns the expected Java class object. If the JSON is not well-formed, parsing cannot be done and MappingJackson2HttpMessageConverter
internally throws a parsing error. Since this exception is not caught by Spring Boot and no object is returned, the REST controller would be unresponsive, having a badly-formed JSON payload.
Here we can override the internal read
method of MappingJackson2HttpMessageConverter
, hack the ReadJavaType
with a customReadJavaType
method, and make it return an internal error when the deserialization fails to parse the JSON input, rather than throwing an exception which is not seen or handled by Spring Boot.
@Override
public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
objectMapper.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
JavaType javaType = getJavaType(type, contextClass);
return customReadJavaType(javaType, inputMessage);
}
private Object customReadJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
try {
if (inputMessage instanceof MappingJacksonInputMessage) {
Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
if (deserializationView != null) {
return this.objectMapper.readerWithView(deserializationView).forType(javaType).
readValue(inputMessage.getBody());
}
}
return this.objectMapper.readValue(inputMessage.getBody(), javaType);
}
catch (InvalidDefinitionException ex) {
//throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
return "Type definition error";
}
catch (JsonProcessingException ex) {
//throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex, inputMessage);
return "JSON parse error";
}
}
This way you can return errors occurring at the deserialization level to Spring Boot, which expects a deserialized object but gets a String
value which can be caught and translated into a ControllerAdvice
handled exception. This also makes it easier to catch JSON parsing errors without using any third party JSON libraries like Gson.
json
rest
java
frameworks
spring
TL;DR : Enum deserialization errors are not caught by org.springframework.validation.Errors
in a Rest Controller
For reference: we didn’t find a clean solution yet as we finally decided that no one should call us wit a bad enum
I have a rest controller that uses org.springframework.validation.Errors
for parameter validations:
@RequestMapping(value = "/vol1/frodo")
public ResponseEntity<Object> simpleMethodUsingPost(
HttpServletRequest httpServletRequest,
@Valid @RequestBody MySimpleObject simpleObject,
Errors errors) {
/* If an error occured, I need to log the object */
if (errors.hasErrors()) {
List<FieldError> fields = errors.getFieldErrors();
doSomething(fields , simpleObject);
}
}
My class MySimpleObject
looks like this:
public class MySimpleObject {
@Valid
@NotNull(message = "anObjectField is a mandatory field")
private EmbeddedObject anObjectField = null;
@Valid
@NotNull(message = "aStringField is a mandatory field")
private String aStringField = null;
@Valid
private MySimpleEnum aSimpleEnum = null;
}
And my enum class MySimpleEnum
is basically a class with two values:
public enum MySimpleEnum{
ORC("ORC"),
URUK("URUK");
private String value;
MySimpleEnum(String value) {
this.value = value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
The validation of this object (and the injection of errors in the springframework Error
object) works well when it’s on a String
or an Object
, but it will fail validation of an enum
(hence an object containing a valid-annoted enum will fail too).
It fails when trying to cast the JSON String
to an enum when the value is not valid:
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error:
Cannot deserialize value of type 'lotr.middleearth.model.MySimpleEnum' from String "HOBBIT"
This deserialization error is caught if I use a ResponseEntityExceptionHandler
and override handleHttpMessageNotReadable
, but then I don’t have access to the different other parameters and can’t use them.
How can I configure either a Validator, enum or springframework Error so that this exception is caught and usable in my controller body?
java spring error-handling enums exception-handling
edited Dec 12 ’18 at 9:40
asked Nov 28 ’18 at 17:52
TL;DR : Enum deserialization errors are not caught by org.springframework.validation.Errors
in a Rest Controller
For reference: we didn’t find a clean solution yet as we finally decided that no one should call us wit a bad enum
I have a rest controller that uses org.springframework.validation.Errors
for parameter validations:
@RequestMapping(value = "/vol1/frodo")
public ResponseEntity<Object> simpleMethodUsingPost(
HttpServletRequest httpServletRequest,
@Valid @RequestBody MySimpleObject simpleObject,
Errors errors) {
/* If an error occured, I need to log the object */
if (errors.hasErrors()) {
List<FieldError> fields = errors.getFieldErrors();
doSomething(fields , simpleObject);
}
}
My class MySimpleObject
looks like this:
public class MySimpleObject {
@Valid
@NotNull(message = "anObjectField is a mandatory field")
private EmbeddedObject anObjectField = null;
@Valid
@NotNull(message = "aStringField is a mandatory field")
private String aStringField = null;
@Valid
private MySimpleEnum aSimpleEnum = null;
}
And my enum class MySimpleEnum
is basically a class with two values:
public enum MySimpleEnum{
ORC("ORC"),
URUK("URUK");
private String value;
MySimpleEnum(String value) {
this.value = value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
The validation of this object (and the injection of errors in the springframework Error
object) works well when it’s on a String
or an Object
, but it will fail validation of an enum
(hence an object containing a valid-annoted enum will fail too).
It fails when trying to cast the JSON String
to an enum when the value is not valid:
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error:
Cannot deserialize value of type 'lotr.middleearth.model.MySimpleEnum' from String "HOBBIT"
This deserialization error is caught if I use a ResponseEntityExceptionHandler
and override handleHttpMessageNotReadable
, but then I don’t have access to the different other parameters and can’t use them.
How can I configure either a Validator, enum or springframework Error so that this exception is caught and usable in my controller body?
java spring error-handling enums exception-handling
edited Dec 12 ’18 at 9:40
asked Nov 28 ’18 at 17:52
0
TL;DR : Enum deserialization errors are not caught by org.springframework.validation.Errors
in a Rest Controller
For reference: we didn’t find a clean solution yet as we finally decided that no one should call us wit a bad enum
I have a rest controller that uses org.springframework.validation.Errors
for parameter validations:
@RequestMapping(value = "/vol1/frodo")
public ResponseEntity<Object> simpleMethodUsingPost(
HttpServletRequest httpServletRequest,
@Valid @RequestBody MySimpleObject simpleObject,
Errors errors) {
/* If an error occured, I need to log the object */
if (errors.hasErrors()) {
List<FieldError> fields = errors.getFieldErrors();
doSomething(fields , simpleObject);
}
}
My class MySimpleObject
looks like this:
public class MySimpleObject {
@Valid
@NotNull(message = "anObjectField is a mandatory field")
private EmbeddedObject anObjectField = null;
@Valid
@NotNull(message = "aStringField is a mandatory field")
private String aStringField = null;
@Valid
private MySimpleEnum aSimpleEnum = null;
}
And my enum class MySimpleEnum
is basically a class with two values:
public enum MySimpleEnum{
ORC("ORC"),
URUK("URUK");
private String value;
MySimpleEnum(String value) {
this.value = value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
The validation of this object (and the injection of errors in the springframework Error
object) works well when it’s on a String
or an Object
, but it will fail validation of an enum
(hence an object containing a valid-annoted enum will fail too).
It fails when trying to cast the JSON String
to an enum when the value is not valid:
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error:
Cannot deserialize value of type 'lotr.middleearth.model.MySimpleEnum' from String "HOBBIT"
This deserialization error is caught if I use a ResponseEntityExceptionHandler
and override handleHttpMessageNotReadable
, but then I don’t have access to the different other parameters and can’t use them.
How can I configure either a Validator, enum or springframework Error so that this exception is caught and usable in my controller body?
java spring error-handling enums exception-handling
edited Dec 12 ’18 at 9:40
asked Nov 28 ’18 at 17:52
TL;DR : Enum deserialization errors are not caught by org.springframework.validation.Errors
in a Rest Controller
For reference: we didn’t find a clean solution yet as we finally decided that no one should call us wit a bad enum
I have a rest controller that uses org.springframework.validation.Errors
for parameter validations:
@RequestMapping(value = "/vol1/frodo")
public ResponseEntity<Object> simpleMethodUsingPost(
HttpServletRequest httpServletRequest,
@Valid @RequestBody MySimpleObject simpleObject,
Errors errors) {
/* If an error occured, I need to log the object */
if (errors.hasErrors()) {
List<FieldError> fields = errors.getFieldErrors();
doSomething(fields , simpleObject);
}
}
My class MySimpleObject
looks like this:
public class MySimpleObject {
@Valid
@NotNull(message = "anObjectField is a mandatory field")
private EmbeddedObject anObjectField = null;
@Valid
@NotNull(message = "aStringField is a mandatory field")
private String aStringField = null;
@Valid
private MySimpleEnum aSimpleEnum = null;
}
And my enum class MySimpleEnum
is basically a class with two values:
public enum MySimpleEnum{
ORC("ORC"),
URUK("URUK");
private String value;
MySimpleEnum(String value) {
this.value = value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
The validation of this object (and the injection of errors in the springframework Error
object) works well when it’s on a String
or an Object
, but it will fail validation of an enum
(hence an object containing a valid-annoted enum will fail too).
It fails when trying to cast the JSON String
to an enum when the value is not valid:
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error:
Cannot deserialize value of type 'lotr.middleearth.model.MySimpleEnum' from String "HOBBIT"
This deserialization error is caught if I use a ResponseEntityExceptionHandler
and override handleHttpMessageNotReadable
, but then I don’t have access to the different other parameters and can’t use them.
How can I configure either a Validator, enum or springframework Error so that this exception is caught and usable in my controller body?
java spring error-handling enums exception-handling
java spring error-handling enums exception-handling
edited Dec 12 ’18 at 9:40
asked Nov 28 ’18 at 17:52
edited Dec 12 ’18 at 9:40
asked Nov 28 ’18 at 17:52
edited Dec 12 ’18 at 9:40
edited Dec 12 ’18 at 9:40
edited Dec 12 ’18 at 9:40
asked Nov 28 ’18 at 17:52
asked Nov 28 ’18 at 17:52
asked Nov 28 ’18 at 17:52
The problem that is occurring is that in the enum MySimpleEnum there is no constant «HOBBIT» the possibilities are «ORC» and «URUK», in the validation question can be used simply as in the example:
@NotNull(message = "Custom message")
private MySimpleEnum aSimpleEnum
answered Nov 28 ’18 at 21:01
Jeremias SantosJeremias Santos
I ended up doing something like that to extract the problematic field in the request :
int start = ex.getMessage().indexOf("["");
int end = ex.getMessage().indexOf(""]");
String fieldName = exception.getMessage().substring(start + 2, end)
The field happens to be at the end of the message between the brackets.
I’m not really proud of that one, it’s messy, but it seems to be the only way with enums.
I guess it would be better to use strings and proper Spring validation instead, since it depends too much on the implementation and may break with future updates.
StackExchange.ifUsing(«editor», function () {
StackExchange.using(«externalEditor», function () {
StackExchange.using(«snippets», function () {
StackExchange.snippets.init();
});
});
}, «code-snippets»);
StackExchange.ready(function() {
var channelOptions = {
tags: «».split(» «),
id: «1»
};
initTagRenderer(«».split(» «), «».split(» «), channelOptions);
StackExchange.using(«externalEditor», function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using(«snippets», function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: ‘answer’,
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: «»,
imageUploader: {
brandingHtml: «Powered by u003ca class=»icon-imgur-white» href=»https://imgur.com/»u003eu003c/au003e»,
contentPolicyHtml: «User contributions licensed under u003ca href=»https://creativecommons.org/licenses/by-sa/3.0/»u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href=»https://stackoverflow.com/legal/content-policy»u003e(content policy)u003c/au003e»,
allowUrls: true
},
onDemand: true,
discardSelector: «.discard-answer»
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave(‘#login-link’);
});
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin(‘.new-post-login’, ‘https%3a%2f%2fstackoverflow.com%2fquestions%2f53525359%2fvalidate-an-enum-with-springframework-validation-errors%23new-answer’, ‘question_page’);
}
);
Post as a guest
Required, but never shown
The problem that is occurring is that in the enum MySimpleEnum there is no constant «HOBBIT» the possibilities are «ORC» and «URUK», in the validation question can be used simply as in the example:
@NotNull(message = "Custom message")
private MySimpleEnum aSimpleEnum
answered Nov 28 ’18 at 21:01
Jeremias SantosJeremias Santos
The problem that is occurring is that in the enum MySimpleEnum there is no constant «HOBBIT» the possibilities are «ORC» and «URUK», in the validation question can be used simply as in the example:
@NotNull(message = "Custom message")
private MySimpleEnum aSimpleEnum
answered Nov 28 ’18 at 21:01
Jeremias SantosJeremias Santos
0
The problem that is occurring is that in the enum MySimpleEnum there is no constant «HOBBIT» the possibilities are «ORC» and «URUK», in the validation question can be used simply as in the example:
@NotNull(message = "Custom message")
private MySimpleEnum aSimpleEnum
answered Nov 28 ’18 at 21:01
Jeremias SantosJeremias Santos
The problem that is occurring is that in the enum MySimpleEnum there is no constant «HOBBIT» the possibilities are «ORC» and «URUK», in the validation question can be used simply as in the example:
@NotNull(message = "Custom message")
private MySimpleEnum aSimpleEnum
answered Nov 28 ’18 at 21:01
Jeremias SantosJeremias Santos
answered Nov 28 ’18 at 21:01
Jeremias SantosJeremias Santos
answered Nov 28 ’18 at 21:01
Jeremias SantosJeremias Santos
answered Nov 28 ’18 at 21:01
Jeremias SantosJeremias Santos
I ended up doing something like that to extract the problematic field in the request :
int start = ex.getMessage().indexOf("["");
int end = ex.getMessage().indexOf(""]");
String fieldName = exception.getMessage().substring(start + 2, end)
The field happens to be at the end of the message between the brackets.
I’m not really proud of that one, it’s messy, but it seems to be the only way with enums.
I guess it would be better to use strings and proper Spring validation instead, since it depends too much on the implementation and may break with future updates.
I ended up doing something like that to extract the problematic field in the request :
int start = ex.getMessage().indexOf("["");
int end = ex.getMessage().indexOf(""]");
String fieldName = exception.getMessage().substring(start + 2, end)
The field happens to be at the end of the message between the brackets.
I’m not really proud of that one, it’s messy, but it seems to be the only way with enums.
I guess it would be better to use strings and proper Spring validation instead, since it depends too much on the implementation and may break with future updates.
0
I ended up doing something like that to extract the problematic field in the request :
int start = ex.getMessage().indexOf("["");
int end = ex.getMessage().indexOf(""]");
String fieldName = exception.getMessage().substring(start + 2, end)
The field happens to be at the end of the message between the brackets.
I’m not really proud of that one, it’s messy, but it seems to be the only way with enums.
I guess it would be better to use strings and proper Spring validation instead, since it depends too much on the implementation and may break with future updates.
I ended up doing something like that to extract the problematic field in the request :
int start = ex.getMessage().indexOf("["");
int end = ex.getMessage().indexOf(""]");
String fieldName = exception.getMessage().substring(start + 2, end)
The field happens to be at the end of the message between the brackets.
I’m not really proud of that one, it’s messy, but it seems to be the only way with enums.
I guess it would be better to use strings and proper Spring validation instead, since it depends too much on the implementation and may break with future updates.
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave(‘#login-link’);
});
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin(‘.new-post-login’, ‘https%3a%2f%2fstackoverflow.com%2fquestions%2f53525359%2fvalidate-an-enum-with-springframework-validation-errors%23new-answer’, ‘question_page’);
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave(‘#login-link’);
});
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave(‘#login-link’);
});
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave(‘#login-link’);
});
Sign up using Email and Password
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
У меня есть приложение для весенней загрузки с java-клиентом, созданным через плагин gradle:
openApiGenerate {
generatorName = "java"
inputSpec = specsYml
outputDir = "$buildDir/generated".toString()
apiPackage = "com.customapi.api"
invokerPackage = "com.customapi.invoker"
modelPackage = "com.customapi.model"
configOptions = [
dateLibrary: "java8",
library : "resttemplate"
]
}
Я выбрал "java8"
как dateLibrary
, поскольку он кажется предпочтительным для проекта с java 1.8.
С помощью этого сгенерированного клиента я выполняю запрос, который возвращает объект, содержащий временную метку. Я получаю следующую ошибку:
java.lang.IllegalStateException: Failed to execute CommandLineRunner
...
Caused by: org.springframework.web.client.RestClientException: Error while extracting response for type [class com.customapi.model.Info] and content type [application/json];
...
Caused by: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.time.OffsetDateTime` from String "2020-07-21T12:12:23.000+0200": ...
...
...
Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.OffsetDateTime` from String "2020-07-21T12:12:23.000+0200": Failed to deserialize java.time.OffsetDateTime: (java.time.format.DateTimeParseException) Text '2020-07-21T12:12:23.000+0200' could not be parsed at index 23
at [Source: (ByteArrayInputStream); line: 1, column: 84] (through reference chain: com.customapi.model.Info["buildTimestamp"])
at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67) ~[jackson-databind-2.10.3.jar:2.10.3]
at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1679) ~[jackson-databind-2.10.3.jar:2.10.3]
at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:935) ~[jackson-databind-2.10.3.jar:2.10.3]
at com.fasterxml.jackson.datatype.jsr310.deser.JSR310DeserializerBase._handleDateTimeException(JSR310DeserializerBase.java:86) ~[jackson-datatype-jsr310-2.10.3.jar:2.10.3]
at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.java:218) ~[jackson-datatype-jsr310-2.10.3.jar:2.10.3]
at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.java:50) ~[jackson-datatype-jsr310-2.10.3.jar:2.10.3]
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129) ~[jackson-databind-2.10.3.jar:2.10.3]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:369) ~[jackson-databind-2.10.3.jar:2.10.3]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159) ~[jackson-databind-2.10.3.jar:2.10.3]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4218) ~[jackson-databind-2.10.3.jar:2.10.3]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3267) ~[jackson-databind-2.10.3.jar:2.10.3]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:269) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
... 17 common frames omitted
Caused by: java.time.format.DateTimeParseException: Text '2020-07-21T12:12:23.000+0200' could not be parsed at index 23
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949) ~[na:1.8.0_151]
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1777) ~[na:1.8.0_151]
at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.java:212) ~[jackson-datatype-jsr310-2.10.3.jar:2.10.3]
... 24 common frames omitted
Соответствующие части рассматриваемого класса Info
:
...
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2020-07-26T14:09:54.137+02:00[Europe/Berlin]")
public class Info {
...
public static final String JASON_PROPERTY_BUILD_TIMESTAMP = "buildTimestamp";
private OffsetDateTime buildTimestamp;
...
public Info buildTimestamp(OffsetDateTime buildTimestamp) {
this.buildTimestamp = buildTimestamp;
return this;
}
public void setBuildTimestamp(OffsetDateTime buildTimestamp) {
this.buildTimestamp = buildTimestamp;
}
...
}
Оба метода установки принимают объекты OffsetDateTime
и не имеют аннотаций, поэтому преобразование должно происходить в другом месте. Входная строка снова будет «2020-07-21T12: 12: 23.000 + 0200». соответствующие зависимости
ext {
swagger_annotations_version = "1.5.22"
jackson_version = "2.10.3"
jackson_databind_version = "2.10.3"
jackson_databind_nullable_version = "0.2.1"
}
dependencies {
compile "io.swagger:swagger-annotations:$swagger_annotations_version"
compile "com.fasterxml.jackson.core:jackson-core:$jackson_version"
compile "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
compile "com.fasterxml.jackson.core:jackson-databind:$jackson_databind_version"
compile "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:$jackson_version"
compile "org.openapitools:jackson-databind-nullable:$jackson_databind_nullable_version"
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
}
Кажется, существует много проблем с jackson и java 8, и большинство решений на этом сайте, похоже, добавляют аннотации. Но я сомневаюсь, что изменение сгенерированного кода — правильное решение. Не упустил ли я важный параметр при создании клиента? Сервер предоставляет неправильный формат? Как я могу это выяснить?
Обновить:
Когда я переключаю dateLibrary
на legacy
, он работает, поэтому я думаю, что получаю правильные данные.
Ошибка в генераторе серверов (jaxrs) https: / /github.com/swagger-api/swagger-codegen/issues/3648#issuecomment-244056314, при котором сервер отправляет сообщения в неверном формате (без двоеточия) date-time
. Мое решение заключалось в использовании устаревшей библиотеки dateLibrary для клиента, которая может обрабатывать неправильный формат.
1 ответ
Лучший ответ
Следуя моему комментарию в вопросе, я понимаю, что вам не нужна аннотация Джексона. Вам просто нужно настроить сеттер. Вот базовая демонстрация:
Предположим следующий класс:
import java.time.OffsetDateTime;
//import com.fasterxml.jackson.annotation.JsonSetter;
import java.time.format.DateTimeFormatter;
public class MyOdt {
private OffsetDateTime odt;
public OffsetDateTime getOdt() {
return odt;
}
//@JsonSetter("odt")
public void setOdt(String odtString) {
final String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSxx";
DateTimeFormatter dtfB = DateTimeFormatter.ofPattern(pattern);
this.odt = OffsetDateTime.parse(odtString, dtfB);
}
}
Класс будет создан из такого фрагмента JSON:
String jsonTest = "{ "odt" : "2020-07-21T12:12:23.000+0200" }";
Картограф объектов:
ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
MyOdt odtTest = objectMapper.readValue(jsonTest, MyOdt.class);
Для справки, вот исходный комментарий в вопросе:
Замечание: это недопустимая строка для анализа с помощью
OffsetDateTime.parse()
, поскольку формат даты и времени по умолчанию предполагает, что смещение будет содержать двоеточие:+02:00
. Итак, это работает:OffsetDateTime.parse("2020-07-21T12:12:23.000+02:00")
2
andrewjames
27 Июл 2020 в 14:43