I have a Spring Boot app using Jackson. I'm not using Jersey just regular Spring MVC. I have a Wrapper Request class:
public class WrapperRequest {
#NotNull
private final Object obj; // some object that corresponds with a JSON object
#JsonCreator
public WrapperRequest(#JsonProperty("wrapper") final Object obj) {
this.obj = obj;
}
public Object getObj() {return obj}
}
The JSON for this would look like:
{
"wrapper":{
//Object data
}
}
The #NotNull from javax isn't working as I excepted. The way I want it to work is that if the consumer sends in a JSON that have a typo like:
{
"wrapperr":{
//Object data
}
}
Jackson will not map my wrapper class because the key in the JSON doesnt match the JsonProperty i.e ("wrapper") (so Object will be null and then I will get NPE later on if I tried to interact with Object. Am I using Jackson wrong? i.e Jackson maps things that it knows about and the rest is null or am I not using the #NotNull annotation correctly?
Jackson is not aware of Bean Validation annotations like #NotNull. For the specific case of "null", you can mark the constructor parameter as #JsonProperty(required = true). More generally, you can use #NotNull on a property as you did and mark your MVC controller parameter with #Valid (which will not cause deserialization to fail but will cause Spring MVC to return a 400 if the validation fails).
Note that you may also be interested in the UNWRAP_ROOT_VALUE feature, which would allow you to eliminate the need for the wrapper class in this particular case.
Related
Hi StackOverflow Community,
I am currently trying to deserialize JSON request bodies provided via Spring Boot #RestController.
The request body contains the following array:
{
...
"productIds": [
"123abc",
"234def"
],
...
}
However, I don't want to deserialize the product IDs into a list of Strings, but rather use a simple wrapper class (for various reasons, including but not limited to additional type safety and validation opportunities). Consequently the class looks like this (Lombok annotations were used to keep the code snippet short):
#Value
#AllArgsConstructor
public class TheRequest {
...
List<ProductId> productIds;
...
}
with ProductId being just a simple wrapper as already said (validation annotations are omitted for the sake of brevity):
#Value
#AllArgsConstructor
public class ProductId{
String id;
}
Looking at Stackoverflow I only found ways to achieve this using rather verbose custom deserialization methods.
However, I am a bit astonished, that Jackson does not provide this functionality out of the box. Consequently it would be great if anyone has any idea if
there is a more elegant way to achieve deserialization of a array of Strings into a List of WrapperObjects, ideally only using Jackson annotations?
there is an elegant way to achieve serialization of such a resulting List of ProductId wrapper objects back into String objects, ideally also using only Jackson annotations? I tried Jacksons #Value but that did not provide the required result.
To me still to verbose but it seems to be a working solution with Jacson 2.14+:
public record PayloadId(String id) {
#JsonCreator(mode = Mode.DELEGATING)
public PayloadId{}
#JsonValue
#Override
public String id() {
return id;
}
}
...and here is the records test https://github.com/FasterXML/jackson-databind/blob/2.14/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordCreatorsTest.java
I have a spring rest API that returns a JSON response from the response class shown below:
public class myResponse {
private String anyString;
private boolean isBoolean;
//getters and setters
}
I am expecting the JSON response to be:
{
"anyString" : "foo",
"isBoolean" : true
}
However, whenever I inspect the browser for the response obtained, I get:
{
"anyString" : "foo",
"boolean" : true
}
Why is the preceding "is" being truncated?
If you're using Spring Boot, then somewhere internally it uses Jackson to transform your object into json string.
you can dive into the logic of ObjectMapper class, but the idea is that it follows JavaBeans convention for accessing fields and getting resulting naming.
So, for boolean property named 'isSth' (via method object.isSth() ) actually represents a field 'sth' for json. If you want to strictly set the name of the field in json, use #JsonProperty annotation
I have the following situation
public class MyCustomForm {
private MyCustomType a;
private MyCustomType b;
}
#RestController
public class AController {
#RequestMapping(...)
public void myMethod(#RequestBody MyCustomForm form){
...
}
}
I want to send in a POST request the necessary data to fill the form. The problem is that MyCustomType is a complex data type and cannot be deserialized from JSON.
The first thing I tried was to write a PropertyEditor so that Spring will know how the make the deserialization from a string. This solution works if I use anything else beside #RequestBody (it works with #PathVariable for example).
I made some research and the reason why #RequestBody is not working is because this annotation generates a proxy which uses its own deserialization rules. Those rules do not interfere with custom PropertyEditors.
The next thing I tried was to use a custom Converter. This solution still didn't solved the issue.
Any other ideas?
I understood that the newest version of jackson (version 2) will know about the custom Converters or PropertyEditors but updating my jackson mapper is not really a solution in my case.
You can use #JsonDeserialize for your MyCustomType classes like
public class MyCustomForm {
#JsonDeserialize(using = MyCustomTypeDeserializer.class)
private MyCustomType a;
#JsonDeserialize(using = MyCustomTypeDeserializer.class)
private MyCustomType b;
}
Some references:
https://fasterxml.github.io/jackson-databind/javadoc/2.3.0/com/fasterxml/jackson/databind/annotation/JsonDeserialize.html
http://www.davismol.net/2015/06/05/jackson-using-jsonserialize-or-jsondeserialize-annotation-to-register-a-custom-serializer-or-deserializer/
http://www.baeldung.com/jackson-custom-serialization
So I am just trying out Jersey for REST services and it seems to we working out fine. I only expose get services and all of the object types that I expose with these services have an immutable object representation in Java. By default Jersey seems to use a parser (JAXB?), requiring a #XmlRootElement annotation for the class that should be parsed, zero-arg constructor and setters.
I have been using Gson with no zero-arg constructor, no setters and final on all fields with no problems at all. Is there any way to accomplish this with Jersey(i.e. the paser it is using)? I have seen solutions with adapter classes that map data from a immutable object to a mutable representation, but this seems like a lot of boilerplate(new classes, more annotations, etc.) if it can be achieved with Gson without anything added.
Note: 1) I have heard people promote using zero-arg constructor and claim that Gson should not work without it. This is not what I am interested in. 2) I really have tried googling this but my keywords might be off. In other words, humiliate me in moderation.
EDIT 1:
My webservice works if I do like this:
#XmlRootElement
public class Code{
private String code; //Silly object just used for example.
public Code(){}
//(G || S)etters
}
With this class exposing the object:
#GET
#Produces(MediaType.APPLICATION_JSON)
public Set<Code> get(#QueryParam("name") String name) { // Here I want to use a class of my own instead of String name, haven't figured out how yet.
return this.codeService.get(name);
}
If I replace the Code with the following, the webservice stops working:
public class Code{
private final String code;
#JsonCreator
public Code(#JsonProperty("code") String code) {
this.code = code;
}
//Getters omitted
}
What I want is to be able to 1) have immutable objects that can be parsed to/from json and 2) Be able to define something like #RequestBody in Spring MVC for my incoming objects.
Actually this could be pretty easy with Genson. You just need the jar and then configure the Genson feature to use constructors with arguments (if you don't want to put annotations on it).
Genson genson = new GensonBuilder().useConstructorWithArguments(true).create();
// and then register it with jersey
new ResourceConfig().register(new GensonJaxRSFeature().use(genson));
Or you can use JsonProperty on the arguments. See the User Guide for more details.
I'm using Jackson's readValue() method on an object mapper to read from a JSON file and convert it into my java object.
eg.
mapperObject.readValue( node, MyTargetClass.class )
Are there any annotations that I can set on MyTargetClass to enforce required attributes? For example, if I have a JSON object with properties ABC,DEF and GHI, and my Json is the following
{
"ABC" : "somevalue"
"DEF" : "someothervalue"
}
I want it to fail somehow, and only succeed on the readValue if it contained ABC, DEF and GHI.
You can mark a property as required with the #JsonProperty(required = true) annotation, and it will throw a JsonMappingException during deserialization if the property is missing or null.
Edit: I received a downvote for this without comment. I'd love to know why, since it does exactly the right thing.
Jackson does not include validation functionality, and this is by design (i.e. that is considered out-of-scope). But what is usually used is Bean Validation API implementation.
The nice thing about this is decoupling between data format handling, and validation logic.
This is what frameworks like DropWizard use; and it's the direction JAX-RS (like Jersey) are taking things for JAX-RS 2.0.
If you want to make sure a json field is provided, you have to use the #JsonProperty(value = "fieldName", required = true) annotation as a parameter to the constructor. But this is not enough, also the Constructor should have #JsonCreator annotation.
For example, if you have a field named 'endPoint' and you want o make sure it is provided in the JSON file, then the following code will throw an exception if it is not provided.
#JsonCreator
public QuerySettings(#JsonProperty(value = "endPoint", required = true) String endPoint) {
this.endPoint = endPoint;
}
I found this link helpful to understand the Jackson annotations. It also well explains why required=true is not enough and counter-intuitive to its name.
If you are neither satisfied with using #JsonProperty(required = true) as it works only with #JsonCreator nor with the use of bean validation then one more way of tackling it would be to catch this in your setter methods for the relevant variables.
You can simply check if the variable is null before setting it and throw an IllegalArgumentException or NullPointerException (as preferred by few people)
Note: It depends on how your POJO is defined too, so please make sure that it is going the setter method route for this solution to work.