Jackson ignore #Size when serializing - java

I have a situation in which I want to return a request to a user if it fails validation along with an appropriate error message. The problem that I've run across is that I'm using Jackson to deal with the JSON request, and the failure in validation also causes Jackson to fail to serialize the message.
For instance, if I have this object:
public class SomeRequest
{
#Size(min=1, max=10)
private String someField;
//getter and setter here
}
...when I go to serialize when the field is invalid (let's say it has 11 characters in it)...
ObjectMapper mapper = new ObjectMapper();
output = mapper.writeValueAsString(someRequestInstance);
...I get a JsonProcessingException (Error(s) validating object). I've confirmed that a valid object has no problem with serialization. So my question is this: How do I get Jackson to ignore the javax.validation annotations when serializing?
Thanks in advance!

By default Jackson does not invoke bean validation (javax.validation.constraints) on JSON serialization. Either default behavior is overridden/customized or there's any kind of interceptor/hook which does that.
Research the stack trace in order to find out where exactly exception occurs and dig around that.

Related

Bean Validation - constructor/factory parameter

I have a dto object which keeps an IP Range using first and last fields. Simple CRUD operations are made with this class using dropwizard (jersey-jackson-hibernate validator)
public class IpRangeDto {
#JsonCreator
public static IpRangeDto fromCidr(#JsonProperty("cidr") String cidr) {
//Resolve CIDR and assign first and last fields
}
#NotNull
#IpAddress // My custom validator
private String first;
#NotNull
#IpAddress
private String last;
}
For the sake of user-friendliness I had decided to add an alternative way to create this object, which is by using CIDR. So the client could send either first and last fields in JSON or only the cidr field. So the way to do it is as above, using #JsonCreator. And it works just fine.
"ipRange":{
"first": "15.0.0.1",
"last": "15.0.0.255",
}
"ipRange":{
"cidr": "15.0.0.0/24"
}
I want to validate this CIDR value that it's the right format so I can return 422 with proper error message. If I throw exception in the constructor/factory method then jersey-jackson returns 400 directly (even if I throw ConstraintViolationException, it's encapsulated by JsonProcessingException).
I could simply ignore the exceptions, and leave the fields empty which will return 422 because of #NotNull constraints but then the error message will not be as clear as it should be.
I tried adding my #Cidr validator next to the #JsonProperty parameter but that doesn't seem to be effective. My understanding is that validation occurs after Jackson is finished with creating Dtos, so with my #JsonCreator approach there might not be any solution to this problem. So I'm open to refactoring suggestions as well.
I am not an expert on the exact integration of Bean Validation into jackson, but I think it is just doing actual property validation. This means as you already pointed out, the entities are created first and then the properties are validated.
Bean Validation (as of version 1.1) also offers so called method validation, in which case you could place your Cidr constraint onto the string parameter of the method, but as said, I don't think that there is an integration in jackson for that.
And one more thing ;-) - static methods and properties are generally excluded from validation in Bean Validation (see also http://beanvalidation.org/1.1/spec/#d0e2815).
Regarding a workaround, one thing comes to mind (even though it feels a bit complicated). Write a custom class level IpRange constraint. In a class constraint you would get passed a IpRangeDto instance and it is up to you to validate the whole object and select the right error message for any violations. Provided you would add a cidr property to the dto which gets set when fromCidr is called, you would have then all information you need for the validation and selection of a proper error message.

How to serialize POJO fields differently based on Accepts header

We have a POJO that contains a collection and have it annotated thus:
#XmlElement(name = "<MyId")
#XmlElementWrapper(name = "MyIds")
private final Set<Long> myIds;
We are using JacksonJaxbJsonProvider in CXF to do the marshalling in our REST service.
The problem we are seeing is that if someone requests application/xml the response is correct, in that the user gets:
<MyIds>
<MyId>123</MyId>
<MyId>456</MyId>
...
</MyIds>
But when application/json is requested, the user gets (note the singular field name):
{
"MyId" : [123, 456, ...]
}
What I want to know is if there's a way to make that plural in the JSON response, and if so, how.
This feels like it may be bug in Jackson but there may be a perfectly good reason this is happening. Also, I realize that if everyone used the same POJO, we wouldn't have to care about what the marshalled text looked like, but in this case, one of the consumers cannot use our POJO.
If you're using version 2.1 or above of Jackson there's a feature which can be enabled to get the behaviour you want
ObjectMapper m = new ObjectMapper()
m.configure(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME, true);

Jackson - Required property?

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.

How can I send the Object class to server with Jackson?

I have a Message class like this:
class Message {
#JsonProperty("content")
Object content;
}
where the content attribute can be a User, a Post, or a String
and I have to send this object to the server and cast the content to the right class.
I'm using Jackson annotations to serialize the JSON, but when I try to cast the content, an error appears, because the attribute content arrives in the server like a LinkedHashMap object.
The error is:
ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/MegaRadarSocial].[Resteasy]] (http-localhost-127.0.0.1-8080-1) Servlet.service() for servlet Resteasy threw exception: org.jboss.resteasy.spi.UnhandledException: java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to br.com.megaradar.megaradarsocial.model.User
I would like a help to casting...
Thanks
As you control both ends (server and client), you could try Genson library http://code.google.com/p/genson/. One of its features allows you to serialize to json and type information, this enables you to deserialize back to the right type.
Genson genson = new Genson.Builder().setWithClassMetadata(true).create();
json = genson.serialize(yourMessage);
// then deserialize it back
Message message = genson.deserialize(json, Message .class);
The serialized json will look like : {"content": {"#class":"package.path.Message", ...the object values...}}
You can even define aliases for the serialized classes
new Genson.Builder().addAlias("message", Message.class)
Important: Note that you need to use the same configuration of genson on both sides. So enable type information with setWithClassMetadata and if you use aliases, you must define the same on the client side.
What you need is #JsonTypeInfo annotation, like so:
class Message {
#JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY property="type")
#JsonProperty("content")
Object content;
}
(you can see http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html for examples)
which would add property "type" with class name as value (there are many alternative ways as well) when serializing, and using that when deserializing.
Thank you for all the answers. But I found another way to convert my Object to any type I want.
I'm using the method convertValue from the ObjectMapper object. Then, I can simulate the casting.
Thanks again

JsonMappingException NOT getting thrown when it should

I have a class in Java that is generically typed. It is supposed to return an object of type T after receiving some json. I am using the following code to create the object:
ObjectMapper mapper = new ObjectMapper();
this.object = mapper.readValue(json, type);
This method throws a JsonMappingException, and should do so if the object isn't of the proper type. The problem I'm running into (when unit testing) is that if I pass in json of an incorrect type, as long as both objects are pojos no exception is being thrown. I am simply getting back an object of the correct type where all it's fields are null.
Why is the exception not getting thrown here? If I pass in some json with a bunch of fields that don't exist on the type of object it should be mapping to, shouldn't I get an exception from that?
You possibly have:
#JsonIgnoreProperties(ignoreUnknown = true)
set somewhere, so jackson doesn't complain about the mismatch.
How do you expect Jackson to know JSON does not represent expected type? JSON data does not have type, beyond just basic Object/Array/scalars structure. So as long as structure is compatible things work, and this is by design.

Categories