I'm using Jackson to serialize and deserialize objects. The problem is that sometimes I want to show a field and sometimes not.
Actually I'm using the #JsonIgnore to avoid the printing of the property when I don't need it. When I need it I'm disabling the Property through
mapper.getSerializationConfig().disable(SerializationConfig.Feature.USE_ANNOTATIONS);
but this will disable also other annotations that I need.
How can I get the result that I need? Using Views? Any example?
A little pojo to understand what I want:
class User {
private String username;
#JsonIgnore
private String password;
// getter setter
}
writeToDB() {
mapper.getSerializationConfig().disable(SerializationConfig.Feature.USE_ANNOTATIONS);
mapper.writeValueAsString(user);
}
and through the REST API you can get the username without the password (thanks to the JsonIgnore)
In the end I've handled this in a different way. I was using Jersey and Guice so was a little hard to find out how, but I did it.
Basically I used the MixIn annotations of Jackson but using Jersey I had to create the ObjectMapper into a Provider and then bind it with Guice.
In this way when I'm using the REST service Jersey will use the ObjectMapper defined in the Provider; when storing the stuff Jackson will use the standard ObjectMapper.
Now some code.
create the PrivateUser class:
public abstract class PrivateUser {
#JsonIgnore abstract String getPassword();
}
create the provider:
#Provider
public class JacksonMixInProvider implements ContextResolver<ObjectMapper> {
#Override
public ObjectMapper getContext(Class<?> aClass) {
final ObjectMapper mapper = new ObjectMapper();
mapper.getSerializationConfig().addMixInAnnotations(User.class, PrivateUser.class);
return mapper;
}
}
and bind it:
bind(JacksonMixInProvider.class).asEagerSingleton();
That's it! :D
Hope this will help someone else to waste less time!
I think you should handle this a different way, by creating a custom Jackson serializer that can selectively serialize/ignore the password.
Annotations like this should be considered immutable at runtime. There may be some reflection trick to extract the JsonIgnore and set the value, but, if so, this would be really heavy-handed.
Related
I know how to use #JsonProperty in jackson API but the below case is different.
I've below json snippet.
{"results":[{"uniqueCount":395}
So, to parse with jackson API the above json, I've written below java pojo class.
package com.jl.models;
import lombok.Data;
#Data
public class Results
{
private int uniqueCount;
}
Later, I got to parse below similar json snippet.
{"results":[{"count":60}
Now, the problem here is I'm unable to parse this json with Results class as it expects a string uniqueCount.
I can easily create another java pojo class having count member variable but I've to create all the parent java classes having instance of Results class.
So, is there any way I can customize Results class having lombok behaviour to parse both the json without impacting each others?
Thanks for your help in advance.
You can use Jackson's #JsonAnySetter annotation to direct all unknown keys to one method and there you can do the assignment yourself:
#Data
public class Results
{
private int uniqueCount;
// all unknown properties will go here
#JsonAnySetter
public void setUnknownProperty(String key, Object value) {
if (key.equals("count")) {
uniqueCount = (Integer)value;
}
}
}
My pojo class is annotated with XmlAccessorType.NONE.
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
public class Human {
#XmlElement(name="name")
private String name;
private int age
}
JSON which im trying to read contains both properties name and age. If i annotate the class with JsonIgnoreProperties(ignoreUnknown = true) everything works However if i try to use annotation XmlAccessorType(XmlAccessType.NONE) intead, jackson throws unknown property exception.
I tried to add JaxbAnnotationIntrospector into objectmapper but it did not help mapper.setAnnotationIntrospector(new JaxbAnnotationIntrospector());
I don't see why XmlAccessorType(XmlAccessType.NONE) would be relevant here,
It would affect discovery of properties available (no auto-discovery), but NOT what to do with JSON/XML properties for which there is no Bean property.
The difference here is probably more due to difference between defaults for JAXB and Jackson: by default, JAXB silently ignores anything it does not recognize. By default Jackson throws an exception when it does not recognize something.
If you want, you can configure ObjectMapper to ignore these problems by default:
napper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
so that behavior is similar to that of JAXB.
I know how I can make Jackson to ignore any additional fields in Json, simply by adding
#JsonIgnoreProperties(ignoreUnknown = true):
#JsonIgnoreProperties(ignoreUnknown = true)
class MyDto {
int someField;
}
But side-effect of this is that Jackson now also accepts incomplete JSON and fills missing fields with nulls.
How can I enforce Jackson to require every field to exist in json and still ignore additional fields in it?
Thank you.
Jackson explicitly does NOT validate logical POJO contents; instead, you are recommended to use Bean Validation (JSR-303, see http://en.wikipedia.org/wiki/Bean_Validation) API implementation; for example one provided by Hibernate project: http://hibernate.org/validator/
This is the approach many frameworks take; for example, DropWizard supports data-binding using Jackson, and then validation (after data-bind, before business logic run) using Bean Validation.
In order to check if all properties needed are available you need to add the required anotation to the property.
#JsonProperty(value = "response", required = true)
public SomeResponse response;
The rest service responds with
<transaction><trxNumber>1243654</trxNumber><type>INVOICE</type></transaction>
or in JSON:
{"transaction":{"trxNumber":1243654,"type":"INVOICE"}}
There is no problems when I use:
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true)
And as resulting class
#JsonRootName("transaction")
public class Transaction {
private String trxNumber;
private String type;
//getters and setters
}
But actually I should use the Transaction class from 3rd party jar, which is exact like above, but has no #JsonRootName("transaction") annotation.
So I end up with
Could not read JSON: Root name 'transaction' does not match expected ('Transaction') for type...
Is there any ways to force Jackson parse to Transaction class without adding any stuff to the Transaction class itself (as I get this file as part of a binary jar)?
I've tried custom PropertyNamingStrategy, but it seems has to do only with field and getter/setter names, but not class names.
Java7, Jackson 2.0.5.
Any suggestions? thanks.
You can do it with mixin feature. You can create simple interface/abstract class like this:
#JsonRootName("transaction")
interface TransactionMixIn {
}
Now, you have to configure ObjectMapper object:
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
mapper.addMixInAnnotations(Transaction.class, TransactionMixIn.class);
And finally you can use it to deserialize JSON:
mapper.readValue(json, Transaction.class);
Second option - you can write custom deserializer for Transaction class.
I annotated a JAXB class with JsonTypeInfo so that I could serialize polymorphic classes easily. However, the annotation does not show up when serialized by Jersey. To be more specific, it shows up when using ObjectMapper but not as a return type from a resource. I am very confused right now as this seems to be a problem with Jersey => Jackson interaction.
To debug things, I used the jsonfromjaxb example from the jersey-samples to localize my problem. I added the following to the Flights class to have it serialize out to #class.
#JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="#class")
I have the following methods available in the resource, one which just returns the JAXB object and one which manually uses ObjectMapper
#GET
#Produces({"application/json"})
public synchronized Flights getFlightList() {
return myFlights;
}
#GET
#Path("/object_mapper")
#Produces({"application/json"})
public synchronized String getFlights() throws IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(myFlights);
}
The result of querying /jsonfromjaxb/flights
{"flight":[{"flightId":"OK123","company":"Czech Airlines","number":123,"aircraft":"B737"},{"flightId":"OK124","company":"Czech Airlines","number":124,"aircraft":"AB115"}]}
The result of querying /jsonfromjaxb/flights/object_mapper
{"#class":"com.sun.jersey.samples.jsonfromjaxb.jaxb.Flights","flight":[{"number":123,"company":"Czech Airlines","aircraft":"B737","flightId":"OK123"},{"number":124,"company":"Czech Airlines","aircraft":"AB115","flightId":"OK124"}]}
Thanks,
Ransom
I think it looks like you aren't using Jackson-based serialization (that is, one that uses ObjectMapper; low-level jackson generator is used for most JSON output, including ones where binding is done differently). If you were, it definitely should look like what you see from explicit use. So it seems to be matter of changing Jersey JSON configuration.