Let's say I have this POJO:
#Data
public class MyPojo {
private boolean isEnabled;
private int timeout;
}
Deserialization from JSON to the above POJO is done with Jackson 2.9. Now, additionally let's say a client sends an "invalid" request. In this context "invalid" means that one of the POJO properties is null or empty. Here, timeout is missing.
{
"isEnabled": true
}
So far I could not find any Jackson deserialization feature which would make this case fail. Does Jackson provide something like "fail if not all properties are present"?
This one won't work:
#JsonProperty(required = true)
You can use constructor-based annotation - it works as a workaround for this unimplemented Jackson feature:
#JsonCreator
public Foo(#JsonProperty(value = "val", required = true) int val) {
this.val = val;
}
Related
I am calling a third party REST endpoint. The request for the thrid party REST endpoint looks like this.
{
"body": {
"accountNumber": "12345"
},
"header": {
"username": "someusername",
"password": "somepassword"
}
}
I have created 3 bean classes
MyRequest.java
#Builder
#JsonDeserialize(builder = MyRequest.MyRequestBuilder.class)
public class MyRequest {
#JsonProperty("header")
private MyHeader header;
#JsonProperty("body")
private MyBody body;
}
MyBody.java
#Getter
#Builder
public class MyBody {
private String accountNumber;
}
MyHeader.java
#Getter
#Builder
public class MyHeader {
private String username;
private String password;
}
I'm creating request object using
MyBody body = MyBody.builder().accountNumber("12345").build();
MyHeader header = MyHeader.builder().username("someusername").password("somepassword").build();
MyRequest request = MyRequest.builder().body(body).header(header).build();
I'm calling the 3rd part REST endpoint using the below code
HttpEntity<MyRequest> httpEntity = new HttpEntity<>(myRequest, httpHeaders);
String url = "someurl";
someResponse = this.restTemplate.postForObject(url, httpEntity,
SomeResponse.class);
I'm getting proper response. But if I remove #Getter annotation from MyHeader and MyBody, 3rd party REST endpoint is getting null values in request. Why #Getter is necessary here. How to make this work without #Getter.
if I remove #Getter annotation from MyHeader and MyBody, 3rd party REST endpoint is getting null values in request. Why #Getter is necessary here. How to make this work without #Getter.
You need to instruct Jackson somehow which data should be included during serialization. The default mechanism is to use getters for that purpose.
That's not the only way.
Alternatively, you can annotate certain fields with #JsonProperty, or change the default visibility of the fields either globally or for a particular type using annotation #JsonAutoDetect and set its property fieldVisibility to ANY (that would make the fields discoverable even in the absence of getters)
The key point is that the information on which data needs to be present in the serialized JSON should be provided somehow, and it doesn't matter how exactly.
Consider a dummy POJO with no getters:
#AllArgsConstructor
public class Foo {
#JsonProperty
private String bar;
}
Property bar would be reflected in the resulting JSON.
ObjectMapper mapper = new ObjectMapper();
Foo foo = new Foo("baz");
String jsonFoo = mapper.writeValueAsString(foo);
System.out.println(jsonFoo);
Output:
{"bar":"baz"}
Now, if we remove #JsonProperty (no getters as before) that's what would happen.
#AllArgsConstructor
public class Foo {
private String bar;
}
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
Foo foo = new Foo("baz");
String jsonFoo = mapper.writeValueAsString(foo);
System.out.println(jsonFoo);
Output:
{}
An empty Bean produces an empty JSON (or raises an exception, depending on configuration). And vise versa, Deserialization of an empty JSON gives an empty Bean.
I have a generic class
#NoArgsConstructor
public class CustomData<T> {
}
Below is the class it is used.
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Recipient {
#NotEmpty(message = "Please provide email details of recipient")
public String email;
public CustomData<?> custom_data;
}
Below is the payload I'm trying to use
"recipients": [
{
"custom_data": {},
"email": "string"
}
]
However I get an error saying com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class
Can someone please help? Thank you for your time
Please add the property SerializationFeature.FAIL_ON_EMPTY_BEANS= false to your object mapper like objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
//or you can add this code to your any class of springboot which have #Configuration annotation on it.
#Bean
public ObjectMapper getMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
return objectMapper;
}
Send Payload as
{
"custom_data": {},
"email": "emailvalue"
}
//Now I am supposing your api is like this
#PostMapping(value = "/show/recipient") /* /preview-email-recipient*/
public ResponseEntity<?> showRecipient(#Valid #RequestBody Recipient recipient){
log.info(String.format("recipient received {%s} ",recipient.toString()));
return new ResponseEntity<>(recipient,HttpStatus.OK);
}
//curl call will be
endpoint will be post call : http://localhost:8080/show/recipient
with requestbody as : {
"customData": {},
"email": "emailvalue"
}
response : {
"customData": {},
"email": "emailvalue"
}
Reason for failure was ?
0
when you return your object i.e. (Recipient in this case) in response it is getting Serialized to json string using ObjectMapper which is used in spring's MessageConverter(i.e Jackson2HttpMessageConverter) bean. Now the error is caused due to how ObjectMapper serializes your class. Your class has 2 field, 1 of type String and 1 of type JSONObject/GenericType. ObjectMapper when serializing fields, tries to find the corresponding serializer based on the field type. There are some out-of-the-box implementation of serializer for known type like String but for your custom type you either need to provide serializer to ObjectMapper bean or have to disable serialization via configuration of set property SerializationFeature.FAIL_ON_EMPTY_BEANS to false.
Now what does SerializationFeature.FAIL_ON_EMPTY_BEANS do??
public static final SerializationFeature FAIL_ON_EMPTY_BEANS
Feature that determines what happens when no accessors are found for a type (and there are no annotations to indicate it is meant to be serialized). If enabled (default), an exception is thrown to indicate these as non-serializable types; if disabled, they are serialized as empty Objects, i.e. without any properties.
Note that empty types that this feature has only effect on those "empty" beans that do not have any recognized annotations (like #JsonSerialize): ones that do have annotations do not result in an exception being thrown.
Feature is enabled by default.
So
1 way was to disable serialization on empty beans.
2nd way you can annotate CustomData class with #JsonSerialize i.e. you
are provinding the mapper which serializer you have to used for this
param.
so Make CustomData class as---
#NoArgsConstructor
#JsonSerialize
public class CustomData<T> {
}
I wrote a Custom Serializer and Custom Deserializer to serialize properties marked with #Confidential annotation.
#Data
public class Person {
private String name;
#Confidential
private String address;
}
The Custom Serializer serializes a POJO with following values:
{ "name": "John Doe", "address": "Kearney St"}
as follows:
{"name":"John Doe", "address": {"value":"IjIwMzEwIDU4dGggTG4gTkUi"}}
The Custom Deserializer is also able to deserialize the JSON back to the Person POJO fine.
However, when I make the fields in the Person POJO final, serialization continues to work, but deserialization fails.
#Data
public class Person {
private final String name;
#Confidential
private final String address;
}
Here's the BeanSerializerModifier implementation:
#AllArgsConstructor
public class CustomDeserializerModifier extends BeanDeserializerModifier {
private final ObjectMapper objectMapper;
#Override
public BeanDeserializerBuilder updateBuilder(final DeserializationConfig config,
final BeanDescription beanDesc,
final BeanDeserializerBuilder builder) {
Iterator<SettableBeanProperty> beanPropertyIterator = builder.getProperties();
beanPropertyIterator.forEachRemaining(settableBeanProperty -> {
final Confidential annotation = settableBeanProperty.getAnnotation(Confidential.class);
if (encryptedProperty != null) {
JsonDeserializer<Object> current = settableBeanProperty.getValueDeserializer();
final SettableBeanProperty newSettableBeanProperty =
settableBeanProperty.withValueDeserializer(
new CustomDeserializer(annotation, current, objectMapper)
);
builder.addOrReplaceProperty(newSettableBeanProperty, true);
}
});
return builder;
}
}
I found that CustomDeserializer, never gets called when the Person POJO fields are final.
Here's the error message:
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: {"name":"John Doe","address":{"value":"IjIwMzEwIDU4dGggTG4gTkUi"}}; line: 1, column: 30] (through reference chain: com.custom.model.Person["address"])
Can a Jackson expert please tell me why my CustomDeserializer isn't getting invoked when the POJO fields are final.
Thank you!
As mentioned, serialization will perfectly work for both mutable and immutable fields. Deserialization issue will only occur when using immutable fields as the BeanDeserializerModifier won't work in such a case.
In the Jackson terminology, immutable fields are named creator properties, meaning they are initialized using creators. See BeanDeserializerBase#resolve.
To correctly handle this use case, the ObjectMapper may be created with a custom DeserializationContext (an extended implementation of the ObjectMapper may also set the protected field related to the deserialization context).
Then, through an override of the method DeserializationContext#handleSecondaryContextualization, it will be possible to change the deserialization to make it work.
There is maybe other possibilities but this one is working fine with encryption.
My REST service returns following JSON
{
"name": "John",
"id" : 10
}
Can I use Jersey to marshall it into following Bean:
public class User{
private String name;
//getter & setter
}
I wanted to do this with following code but it doesn't work
WebResource webResource = client.resource(url);
webResource.accept(MediaType.APPLICATION_JSON_TYPE);
User user = webResource.get(User.class);
Is this even possible or I have to implement full JSON structure in Java Beans to get it work?
I know that I can parse this JSON with Jackson and any other methods.
With Jackson, easiest way is to configure ObjectMapper like so:
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
Check this sample provider
package com.company.rest.jersey;
#Provider
#Component
#Produces({MediaType.APPLICATION_JSON})
public class JacksonMapperProvider implements ContextResolver<ObjectMapper> {
ObjectMapper mapper;
public JacksonMapperProvider(){
mapper = new ObjectMapper();
mapper.configure(Feature.INDENT_OUTPUT, true);
// Serialize dates using ISO8601 format
// Jackson uses timestamps by default, so use StdDateFormat to get ISO8601
mapper.getSerializationConfig().setDateFormat(new StdDateFormat());
// Deserialize dates using ISO8601 format
// MilliDateFormat simply adds milliseconds to string if missing so it will parse
mapper.getDeserializationConfig().setDateFormat(new MilliDateFormat());
// Prevent exceptions from being thrown for unknown properties
mapper.configure(
DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,false);
}
#Override
public ObjectMapper getContext(Class<?> aClass) {
return mapper;
}
}
With Jackson :
You have two options:
Jackson works on setters-getters of fields. So, you can just remove getter of field which you want to omit in JSON. ( If you don't need getter at other place.)
Or, you can use the #JsonIgnore annotation of Jackson on getter method of that field and you see there in no such key-value pair in resulted JSON.
#JsonIgnore
public int getSecurityCode(){
return securityCode;
}
In your bean, add the annotation #JsonIgnoreProperties(ignoreUnknown = true) at the class level and it should skip the id property in the JSON since it's not present in the bean.
#JsonIgnoreProperties(ignoreUnknown = true)
public class User{
private String name;
//getter & setter
}
(See http://wiki.fasterxml.com/JacksonAnnotations for details)
i have an class with the following annotations:
class A {
public Map<String,List<String>> references;
#JsonProperty
public Map<String,List<String>> getReferences() {
...
}
#JsonIgnore
public void setReferences(Map<String,List<String>>) {
}
...
}
}
What I try is to ignore the json on deserialization. But it doesn't work. Always when JSON String arrives the Jackson lib fill the references attribute. If I use only the #JsonIgnore annotation the getter doesn't work. Are there any solutions for this problem?
Thanks
I think there are two key pieces that should enable you to have "read-only collections" as desired. First, in addition to ignoring the setter, ensure that your field is also marked with #JsonIgnore:
class A {
#JsonIgnore
public Map<String,List<String>> references;
#JsonProperty
public Map<String,List<String>> getReferences() { ... }
#JsonIgnore
public void setReferences(Map<String,List<String>>) { ... }
}
Second, in order to prevent the getters from being used as setters, disable the USE_GETTERS_AS_SETTERS feature:
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.USE_GETTERS_AS_SETTERS);
As of Jackson 2.6, there is a new and improved way to define read-only and write-only properties, using JsonProperty#access() annotation. This is recommended over use of separate JsonIgnore and JsonProperty annotations.
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
public Map<String,List<String>> references;
You have to make sure there is #JsonIgnore annotation on the field level as well as on the setter, and getter annotated with #JsonProperty.
public class Echo {
#Null
#JsonIgnore
private String doNotDeserialise;
private String echo;
#JsonProperty
public String getDoNotDeserialise() {
return doNotDeserialise;
}
#JsonIgnore
public void setDoNotDeserialise(String doNotDeserialise) {
this.doNotDeserialise = doNotDeserialise;
}
public String getEcho() {
return echo;
}
public void setEcho(String echo) {
this.echo = echo;
}
}
#Controller
public class EchoController {
#ResponseBody
#RequestMapping(value = "/echo", consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
public Echo echo(#RequestBody #Valid Echo echo) {
if (StringUtils.isEmpty(echo.getDoNotDeserialise())) {
echo.setDoNotDeserialise("Value is set by the server, not by the client!");
}
return echo;
}
}
If you submit a JSON request with a “doNotDeserialise” value set to something, when JSON is deserialised to an object it will be set to null (if not I put a validation constraint on the field so it will error out)
If you set the “doNotDeserialise” value to something on the server then it will be correctly serialised to JSON and pushed to the client
I used #JsonIgnore on my getter and it didn't work and I couldn't configure the mapper (I was using Jackson Jaxrs providers). This worked for me:
#JsonIgnoreProperties(ignoreUnknown = true, value = { "actorsAsString",
"writersAsString", "directorsAsString", "genresAsString" })
I can only think of a non-jackson solution, to use a base class that does not have references for the mapping and then cast to the actual class:
// expect a B on an incoming request
class B {
// ...
}
// after the data is read, cast to A which will have empty references
class A extends B {
public Map<String,List<String>> references;
}
Why do you even send the References if you don't want them?
Or is the incoming data out of your hands and you just want to avoid the mapping exception telling you that jackson cannot find a property to set for incoming references? For that we use a base class which all of our Json model classes inherit:
public abstract class JsonObject {
#JsonAnySetter
public void handleUnknown(String key, Object value) {
// for us we log an error if we can't map but you can skip that
Log log = LogFactory.getLog(String.class);
log.error("Error mapping object of type: " + this.getClass().getName());
log.error("Could not map key: \"" + key + "\" and value: \"" + "\"" + value.toString() + "\"");
}
Then in the POJO you add #JsonIgnoreProperties so that incoming properties will get forwarded to handleUnknown()
#JsonIgnoreProperties
class A extends JsonObject {
// no references if you don't need them
}
edit
This SO Thread describes how to use Mixins. This might be the solution, if you want to keep your structure exactly as it is, but I have not tried it.