Fasterxml Jackson databind MismatchedInputException - java

I am using com.fasterxml.jackson.databind in a spring boot application. When I send a request to my endpoint I receive the following exception:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of
org.pacakge.domain.controller.Mycontroller (although at least one Creator exists): cannot
deserialize from Object value (no delegate- or property-based Creator)\n at
[Source: (PushbackInputStream); line: 2, column: 3] Is the body of the request formatted correctly?
My controller processes a request body that has the following structure:
{
"portalId": 123,
"objectType": "TYPE",
"objectTypeId": "0-3",
"objectId": 123,
"properties": { ... }
}
The only property that I need is objectId. I've constructed a class to process this object like so:
#lombok.Value
private static class MyObject {
#JsonAlias("objectId")
private final String dealId;
}
I've designed a controller that looks like this
#Slf4j
#RestController
#RequestMapping(path = "/entrypoint")
public class MyController {
#Autowired
public MyController(){}
/**
* REST endpoint handles MyObject
*/
#PostMapping(value = "/endpoint")
public void handleRequest(
#Valid #RequestBody MyObject command
) {
log.debug(command.getDealId());
}
#lombok.Value
private static class MyObject {
#JsonAlias("objectId")
private final String dealId;
}
}
What is interesting about this problem is that my request is processed just fine if I change MyObject to the following structure:
#lombok.Value
private static class MyObject {
#JsonAlias("objectId")
private final String dealId;
private final JSONObject properties; // TODO we shouldn't need this. Fix.
}
I cannot seem to figure out what the issue is. I would love some help on this problem. Maybe there is annotation that I am missing? I am hoping someone else has experienced this issue. I haven't found any information on it by just searching the web.

I added the following line to lombok.config in the root directory of the project:
lombok.anyConstructor.addConstructorProperties=true
And after that managed to deserialize your JSON using this DTO using #JsonIgnoreProperties annotation:
#Value
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyObject {
#JsonProperty("objectId")
String dealId;
}

Related

Unrecognized field during deserialization despite #SerializedName annotation

I am fetching some data via a REST service, but I am getting this error when deserializing the response :
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "ResultSet Output" (class com.HolderCollectionWrapper), not marked as ignorable (one known property: "holders"]) at [Source: java.io.ByteArrayInputStream#74efa7bd; line: 1, column: 22] (through reference chain: com.HolderCollectionWrapper["ResultSet Output"])
This is my code :
response = restTemplate.exchange(requestUrl, HttpMethod.GET, request, HolderCollectionWrapper.class);
public class HolderCollectionWrapper {
#SerializedName("ResultSet Output")
private List<Holder> holders;
public List<Holder> getHolders() {
return holders;
}
public void setHolders(List<Holder> holders) {
this.holders = holders;
}
}
This is the JSON I am getting :
{
"ResultSet Output": [
{...}, {...}, {...}
]
}
Despite the #SerializedName("ResultSet Output"), it's not working, why ?
#SerializedName is a gson annotation and you are using jackson library for serialization.
The jackson annotation for field name is #JsonProperty
Try:
#JsonProperty("ResultSet Output")
private List<Holder> holders;
This happens because the SerializedName("ResultSet Output") gson annotation indicates that the holders will be serialized with the ResultSet Output name like the json example you post; to deserialize it with jackson you have to use the JsonProperty annotation, specifying the ResultSet Output name applied on the setter to avoid possible conflicts with the gson library used for serialization:
public class HolderCollectionWrapper {
#SerializedName("ResultSet Output")
private List<Holder> holders;
public List<Holder> getHolders() {
return holders;
}
#JsonProperty("ResultSet Output")
public void setHolders(List<Holder> holders) {
this.holders = holders;
}
}

Make Spring Boot JSON enum deserialization strict, so it does not silently convert invalid values into null

I am using Java Spring Boot #RestController with an object containing enum fields.
Spring automagically deserializes the JSON to the MyRequest object.
#RestController
public class MyController {
#PostMapping(path = "/operation")
public ResponseEntity<MyResponse> operation(#Valid #RequestBody MyRequest request) {
...
}
}
public class MyRequest {
private MyEnum1 field1;
private MyEnum2 field2;
private MyEnum3 field3;
private MyEnum4 field4;
private MyEnum5 field5;
private MyEnum6 field6;
... // really a lot of various enum fields!
}
public enum MyEnum1 {
VAL1, VAL2, VAL3;
}
The problem is that if the JSON contains completely invalid value of the enum field, the deserializer silently converts them to null, without any exception.
{
"field1": "BLAHBLAH",
...
}
This is user-unfriendly and treacherous.
I know that I may write custom JSON deserializers for each enum, but the solution is cumbersome and non-elegant.
Is there a way to globally set the JSON enum deserializer to a "strict mode", so if the value is invalid it throws an exception? If so, how and where?
That feature should be disabled by default.
But if you want to set it explicitly you can do it like this:
in your properties:
spring.jackson.deserialization.read-unknown-enum-values-as-null=false
or as an alternative in a configuration class (actually any bean would work, just make sure it happens early):
#Autowired
public void configureJackson(ObjectMapper objectMapper) {
objectMapper.disable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
}
Because it should actually be like this by default, I am wondering why it is not for you. Do you enable it somewhere? Which Spring Boot version are you using?

Property and Alias mapping don't seem to work

I have a simple json like this
{
"someReports":[
{
"reportName": "PR123",
"fields": [
]
},
{
"reportName": "PR234",
"fields": []
}
]
}
I have a class that looks somewhat like this inside which getSomeReports() is defined.
class AHeckLotOfReports {
private String someString;
private List<SomethingElse> some;
..
#JsonProperty("someReports")
private List<SomeReports> someReports;
}
//POJO:
class SomeReport {
String reportName;
List<Field> fields;
...
}
//REST Controller looks like this. some injection code is cleaned up
#Api(tags = “SomeReport”)
#Controller
#ThreadSafe
#RequestMapping(“/report/v2")
public class ReportController{
#ApiOperation(value = "Create a new report.”)
#RequestMapping(value = “/report”, method = RequestMethod.POST)
#ResponseBody
public ReportResponse addReport(
#Nonnull final AuthorizationToken authorizationToken,
#Nonnull#RequestBody final AHeckLotOfReports reportRequest) {
final Report report=this.reportService.addReport(
authorizationToken,
reportRequest.getName(),
reportRequest.isEnabled(),
reportRequest.getConfiguration().or(ReportConfiguration.empty()),
reportRequest.getNewConfiguration(),
reportRequest.getDefinition());
return ReportResponse.fromReport(report);
}
I haven't been able to get this working. I do get the structure intact but reportName comes up blank, the fields array comes up empty.
I have tried JsonAlias("someReports","some_reports") and that seems to make no difference.
Anything comes out as an obvious "duh" in this ?
EDIT: My apologies, I did realize I have not provided the entire context. I AM able to deserialize the simple POJO with ObjectMapper. But the class AHeckLotOfReports is used as request object in REST endpoint for a POST and this is where the problem surfaces
Software: jackson.core 2.9.9, jackson.datatype 2.9.8 and JDK 8.0, Spring-Boot 2.2.6

Jackson - #JsonCreator return generics

I want to integrate vavr validation library in my command dto's in a way that when command dto is deserialized from request, return type of the static factory will be Try but jackson is throwing following error :
Type definition error: [simple type, class com.foo.command.FooCommand]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of com.foo.command.FooCommand (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
Here is FooCommand
#AllArgsConstructor(access = AccessLevel.PRIVATE)
public final class FooCommand {
private String foo;
private String bar;
#JsonCreator
public static Try<FooCommand> of(
#JsonProperty("foo") String foo,
#JsonProperty("bar") String bar
) {
return Try.of(() -> {
//Validate values
});
}
}
I am using spring 5 and it's annotated to deserialize request body automatically into controller parameter.
Is something like this possible ? Thanks in advance.
I had a similar problem that I fixed by using Converters: Using Jackson, how can I deserialize values using static factory methods that return wrappers with a generic type?
I haven't yet found how to apply the converters automatically, so you have to annotate every occurrence of the wrapped type in your requests.
public class Request {
#JsonDeserialize(converter = FooCommandConverter.class)
Try<FooCommand> command;
}
You can write a Converter like so:
public class FooCommandConverter
extends StdConverter<FooCommandConverter.DTO, Try<FooCommand>> {
#Override
public Try<FooCommand> convert(FooCommandConverter.DTO dto) {
return FooCommand.of(
dto.foo,
dto.bar
);
}
public static class DTO {
public String foo;
public String bar;
}
}

Can't map second level json value with Spring Webflux Webclient

I am consuming the public API for crypto currencies in Mexico: https://api.bitso.com/v3/available_books/ that returns a json like this one:
"success": true,
"payload": [
{
"book": "btc_mxn",
"minimum_price": "500.00",
"maximum_price": "16000000.00",
"minimum_amount": "0.000075",
"maximum_amount": "500.00000000",
"minimum_value": "5",
"maximum_value": "10000000.00"
},
{
"book": "eth_btc",
"minimum_price": "0.00000100",
"maximum_price": "5000.00000000",
"minimum_amount": "0.00000100",
"maximum_amount": "1000.00000000",
"minimum_value": "0.00000100",
"maximum_value": "2000.00000000"
},
and the code that consumes it with Webclient is:
#Override
public Mono<Coins> getCoins() {
return webClient.get().uri("https://api.bitso.com/v3/available_books/")
.accept(MediaType.APPLICATION_JSON)
.retrieve().bodyToMono(Coins.class);
}
The POJOs that are trying to bind it are:
#Data
public class Coins {
#JsonProperty("success")
private String success;
#JsonProperty("playload")
private List<Coin> playload;
and
#Data
public class Coin {
#JsonProperty("book")
private String book;
#JsonProperty("minimum_amount")
private String minimumAmount;
#JsonProperty("maximum_amount")
private String maximumAmount;
#JsonProperty("minimum_price")
private String minimumPrice;
#JsonProperty("maximum_price")
private String maximumPrice;
#JsonProperty("minimum_value")
private String minimumValue;
#JsonProperty("maximum_value")
private String maximumValue;
So far, it only maps like this
"success": true,
"payload": null
You need to have no-args construct and change the word playload to payload :)
I don't think this is a WebFlux issue, but rather a Jackson + Lombok issue.
What happens if you try to deserialize that payload with raw ObjectMapper?
I think Jackson requires an all args constructor annotated with #JsonCreator or ask Lombok to create a #NoArgConstructor for that class. In any case, rewriting your Coin class as a regular Java class should work.
Also, your Coins class has a typo since it's trying to get playload instead of payload.
FIXED: Typo at property name playload instead of payload

Categories