Consider the following example:
#JsonIgnoreProperties(ignoreUnknown = true)
class ModelA {
private ModelB modelB;
}
ModelB introduces two overloaded methods such as setProperty(String) and setProperty(Object). This confuses Jackson's ObjectMapper complaining about "confliciting setter definitions":
Caused by: java.lang.IllegalArgumentException:
Conflicting setter definitions for property "property": ModelB#setProperty(1 params) vs ModelB#setProperty(1 params)
I'm aware that if I can use inheritance then I can use #JsonIgnore as in proposed in this answer. I'm also aware that a bottom-line solution would be to develop a custom Jackson deserializer (although very complex in my case). But, I'd like to know if there is a workaround for this if I'm restricted to use composition?
Related
I am using jackson as part of serializing and deserializing in my project (Spring Java).
In normal scenarios where I have interface(contract) acting as field in POJO,
then I use #JsonTypeInfo and #JsonSubTypes to achieve deserialization in polymorphic cases.
But, right now, I have scenariio something like this:
public class classA {
private contractA fieldA;
//constructor and getter-setters.
}
then,
public interface contractA {
}
and finally,
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(/* concrete-class1 as name-value */),
#JsonSubTypes.Type(/* concrete-class2 as name-value */),
})
public interface contractB extends contractA {
//contract methods.
}
Now, when classA is passed as controller request body and I pass fieldA as concrete-class1 or concrete-class2,
JsonSubTypes are not being used by jackson to deserialize into one of them.
The reason why I did this and had two contracts is due to package dependencies. contractB
is in different package as of contractA's.
How can I configure on contractA using jackson that this class has its JsonSubTypeInfo specified in its subclasses.
Or, any other libraries or approaches are also welcomed.
Thank you !
This problem is later on solved by introducing our own custon JsonTypeInfo.
When the application is under deployment, we fetch all subclasses which is present in the JsonTypeInfo annotation (jackson like custom annotaion) and maintain a data-structure, that will be used while serializing and deserializing. This process is somewhat similar to the Jackson one (in addition to lookup for nested hierarches as well).
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;
ObjectMapper's readValue(InputStream in, Class<T> valueType) function requires the Class. But how do I use it if the class I am passing internally, is having some Interface as data member.
although I can understand the reason behind this exception, as Jackson is not getting the concrete class of the internal Interface of the passed class, but my question is how to resolve it?
how do I deserialize it then? The class I am trying to deserialize is:
class BaseMetricImpl<N> implements Metric<N> {
protected MetricValueDescriptor descriptor;
}
Here MetricValueDescriptor is an interface, so this gives me following error : -
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of MetricValueDescriptor, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
at [Source: java.io.ByteArrayInputStream#2ede2c9f; line: 1, column: 2] (through reference chain: SingleValueMetricImpl["descriptor"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:624)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:115)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:375)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:308)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2793)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1989)
Jackson obviously cannot construct the MetricValueDescriptor object since it is an interface. You will need to have additional information in your json and in your ObjectMapper to tell jackson how to construct an object out of it. Here is one way to do it, assuming MVDImpl is a concrete class which implements MetricValueDescriptor:
You can tell Jackson the required type information through a field in the json itself, say "type". To do this, you need to use JsonTypeInfo and JsonSubTypes annotations in your interface. For example,
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#Type(value = MVDImpl.class, name = "mvdimpl") })
interface MetricValueDescriptor
{
...
}
You will need to add a "type":"mvdimpl" field in your json as well.
I was going to point you to the official doc for more info, but then I found an excellent blog covering this topic - Deserialize JSON with Jackson. It covers this topic pretty comprehensively and with examples. So you should definitely read it if you need more customisation.
I see it going one of two ways, but they both require you manually create a concrete class that implements your interface.
Use #Hari Menon's answer and use #JsonSubTypes. This works if you can introduce a type field or something else to trigger which implementation to use.
Use #JsonDeserialize to tell jackson what concrete class it uses by default.
#JsonDeserialize(as = MVDImpl.class)
interface MetricValueDescriptor
{
...
}
Here's a more thorough explanation: https://zenidas.wordpress.com/recipes/jackson-deserialization-of-interfaces/
And the docs: https://fasterxml.github.io/jackson-databind/javadoc/2.8/com/fasterxml/jackson/databind/annotation/JsonDeserialize.html
You don't need to alter the code, you can set it programmatically on the mapper:
static setup() {
final var simpleModule = new SimpleModule()
.addAbstractTypeMapping(<Interface>.class, <Implementation>.class);
objMapper = new ObjectMapper()
.registerModule(new Jdk8Module()) // You probably want this as well
.registerModule(simpleModule);
}
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.
Does naybody knows a way to use Jersey's GET method to return a JSON that returns only some fields of an entity instead of all?
Does anybody know a way to use Jersey's GET method to return a JSON that returns only some fields of an entity instead of all?
E.g. in the following class I want to receive (with POST) values for 'name' and for 'confidential', buy while returning (with GET) I only need 'name' value, not 'confidential'.
#Entity
#Table(name = "a")
#XmlRootElement
#JsonIgnoreProperties({"confifentialInfo"})
public class A extends B implements Serializable {
private String name;
#Basic(optional = false)
private String confifentialInfo;
// more fields, getters and setters
}
If you are using the JAXB approach, you can mark fields with #XmlTransient to omit them. If you are using POJO mapping or want to exclude fields only for some requests, you should construct the JSON with the low level JSON API.
If you are using Jackson, you can use the annotation #JsonIgnore for methods
Marker annotation similar to javax.xml.bind.annotation.XmlTransient
that indicates that the annotated method is to be ignored by
introspection-based serialization and deserialization functionality.
That is, it should not be consider a "getter", "setter" or "creator".
And #JsonIgnoreProperties for properties
Annotation that can be used to either suppress serialization of
properties (during serialization), or ignore processing of JSON
properties read (during deserialization).