Jackson: ignore the #JsonIgnoreProperties annotation - java

I have a class that I serialize and I use the #JsonIgnoreProperties at the class level to exclude some fields from it.
Lately I have an use case where I need those fields serialized.
Is there a way to make a writer/reader that ignores the annotation?
I was looking into #JsonView but it seems #JsonIgnoreProperties takes precedence over it.

#JsonFilter could help you in this case.
By defining custom json filter, Jackson will dynamically resolve filter given class uses, dynamically, allowing per-call reconfiguration of filtering.
You can find detailed explanation and usage example here
Some usefull information about dynamic ignoral you find here

I have come up with a solution, don't know if it the best one but gets the job done ...
I ended up using #JsonView.
So I have 2 views like this:
public class Views {
public static class Public { }
public static class Extended extends Public { }
}
and the default Spring mapper configured as
mapper.setConfig(mapper.getSerializationConfig().withView(Views.Public.class));
mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
and the class looks like
public class Foo{
String name;
#JsonView(Views.Extended.class)
String title;
...}
By setting up a default view on the object mapper it causes it to ignore all the other not specified views. The fields with no annotation will always be serialized, as per config.
Then, when I need the whole class to be serialized I use:
objectMapper.writer().withView(Views.Extended.class).writeValueAsString(value);

Related

Using Jackson Mixins to selectively ignore only provided properties

I have the following situation:
public class A {
private String someProperty;
private String anotherProperty;
public A() {}
// getter/setter
An ObjectMapper configuration as follows (enabled by default, but worth noting to get the point of the question across):
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
and an input JSON that looks as follows:
{
"someProperty": "someValue",
"anotherProperty":"anotherValue",
"unwantedProperty":"unwantedValue"
}
When deserializing this JSON using objectMapper.readValue(bytes, A.class), as expected, it gives me an exception:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "unwantedProperty"
Now, what I want to do, is the following:
Only deserialize properties that are explicitly provided by A.class
Use a MixIn to ignore all unwanted, known properties. So for example, I know that unwantedProperty is part of the JSON, but I don't need it, so I want to ignore it.
Still raise an UnrecognizedPropertyException if a new, unknown property suddenly appears in my JSON.
The reason that I wish to use something like a MixIn class for this is that in reality, the input JSON has several dozens of fields. I would prefer not to clutter my A.class with dozens and dozens of unused properties with #JsonIgnore on them, so that it only contains the fields that I really want. If a new property unexpectedly does come along, I want to be forced to have a look at it.
I thought that I could allow this behaviour by using a MixIn as follows:
public abstract class AMixIn {
#JsonIgnore private String unwantedProperty;
together with:
objectMapper.addMixIn(A.class, AMixIn.class);
but this seemingly has no effect. I've also tried creating getters in AMixIn and giving those #JsonIgnore, but this also has no result.
Am I using MixIns incorrectly here? Is what I'm trying to do even possible (as described in the 3 points above)? Or, is there a better way to do this?
I've tried searching, but my use case is a bit esoteric, so I haven't had much luck.
Answer to question 1:
You can instruct Jackson to ignore unknown properties.
I tend to configure the ObjectMapper to ignore them,
here is some sample code:
private ObjectMapper mapper;
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
// This matches the Fuse Mapper configuration.
builder.featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
mapper = builder.build();
You can also annotate the class to ignore unknown properties.
Here is some sample code:
#jsonignoreproperties(ignoreunknown = true)
public class A
{
...
Note about question 2:
In order for the use-mixin-to-ignore-fields-in-json strategy to work,
the fields to be ignored must exist in the class.
In your case,
this means that class A must have a field "unwantedProperty" for the mixin to work correctly.
Direction to solve questions 2 and 3
You cannot use the MixIn feature of Jackson to solve either
question 2 or question 3.
Instead,
you will need to write a custom deserializer.
I suggest that you also use a custom Jackson annotation
that configures a list of ignored-unrecognized-fields
and have your custom deserializer only throw the exception
for unrecognized fields that are not part of the
ignored-unrecognized-fields list.
Well, shows how well I can search; as always, Jackson provides some way of doing whatever needs doing. In case it might help someone else:
There exists the #JsonIgnoreProperties annotation which, in addition to the ignoreUnknown property (that I already knew), supports a list of properties (via value()) to ignore during de-/serialization (which I did not know).
This is not quite the same as the intended solution above, but having these properties inside of #JsonIgnoreProperties({ ... }) in the class header instead of the class body is a good enough compromise for me.
So, the solution would be:
#JsonIgnoreProperties({"unwantedProperty"})
public class A {
// same as above...
}
Still, if there is a MixIn solution which can completely decouple these things, I'd still like to see it. I'll accept my own answer if nothing comes up in a few days.

Get outer class by member annotation

I have a class with custom annotation for one of class field:
public class Test {
#CustomAnnotation
private String name;
...
}
I just want to know if it possible to get Class<Test> by this annotation? Can't find any suitable api..
public Class<?> getOuterClass(CustomAnnotation annotation) {
...
}
#CustomAnnotation is declared as #Retention(RetentionPolicy.RUNTIME)
No, annotation does not store any data about where it was declared.
Also annotation can work just like any normal interface, so someone can implement annotation in class an make instances of it that were never used as annotations.
You need either include that information yourself, by adding parameter to annotation and then using it #CustomAnn(Test.class) or when reading annotations just remember and include that information yourself in some other object.

Is there a way that I donot need add #JsonProperties for boolean field when mapping json to pojo using jackson&spring mvc

I cannot find a way to fix this issue, but... maybe it's not an issue.
I use Extjs as the front-end and Spring MVC as backend, the Ajax request looks like:
{"isOk": true}
The Mapping DTO is:
public class TestDTO implements Serializable {
private static final long serialVersionUID = -6074462313103219627L;
private Boolean isOK;
public Boolean isOk(){...}
Public void setOk(Boolean isOk){...}
}
The get/set method be generated by intellij idea, as you can imagine that jackson works fine if I add #JsonProperty("isOk") under the "setOk" method.
But I have a lot of ***DTO objects, so is there a convenient method to reslove this issue? thanks.
I have checked the "com.fasterxml.jackson.databind.SerializationFeature" class, and didn't find any config which like the "compatible_boolean_and_ignore_is_keyword" etc..
I didn't test it, but might be helpful for your case:
https://stackoverflow.com/a/35088196/677937
Basically, try to rename your getter/setter to:
getIsOk / setIsOk
It should then serialize/deserialize json in form of {"isOk": ... }
It's been some time since I used spring, but if I recall correctly you have to
annotate the class with #Entity
implement the Serializable interface (class DTO implements Serializable)
provide a default constructor

Spring+Jackson: How to set visibility of a class without using #JsonAutoDetect annotation

In my spring-boot application, I have a global configuration on Jackson's ObjectMapper which told Jackson not to serialize object by fields but getters:
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder()
{
return new Jackson2ObjectMapperBuilder()
{
#Override
public void configure(ObjectMapper objectMapper)
{
super.configure(objectMapper);
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.GETTER, Visibility.PUBLIC_ONLY);
objectMapper.setVisibility(PropertyAccessor.IS_GETTER, Visibility.PUBLIC_ONLY);
}
};
}
However, now I'm dealing with a special case. I need to serialize a class which is not written by myself (a class form dependnecy library). Since the class does not declared getters, Jackson will ignore those fields.
Here's how the external class look like:
public class DirectionsResult
{
public GeocodedWaypoint geocodedWaypoints[];
public DirectionsRoute routes[];
}
Although using #JsonAutoDetect annotation can customerize a class's visibility for Jackson, this does not work with external classes.
So how can I set visibility of a class without using #JsonAutoDetect annotation and also not to change the global configuration?
You should be able to use jacksons MixIn feature. With this approach you can control all the configuration of a class by another class definition of your choice.
https://github.com/FasterXML/jackson-docs/wiki/JacksonMixInAnnotations
You can also check out one of my github projects to see the use of that feature:
https://github.com/Antibrumm/jackson-antpathfilter

Jackson JSON library: how to instantiate a class that contains abstract fields

I want to convert a JSON string into java object, but the class of this object contains abstract fields, which Jackson can't instantiate, and doesn't produce the object. What is the easiest way to tell it about some default implementation of an abstract class, like
setDefault(AbstractAnimal.class, Cat.class);
or to decide about the implementation class based on JSON attribute name, eg. for JSON object:
{
...
cat: {...}
...
}
i would just wite:
setImpl("cat", Cat.class);
I know it's possible in Jackson to embed class information inside JSON, but I don't want to complicate the JSON format I use. I want to decide what class to use just by setting default implementation class, or by the attribute name ('cat') - like in XStream library, where you write:
xStream.alias("cat", Cat.class);
Is there a way to do so, especially in one line, or does it require some more code?
There are multiple ways; before version 1.8, simplest way is probably to do:
#JsonDeserialize(as=Cat.class)
public abstract class AbstractAnimal { ... }
as to deciding based on attribute, that is best done using #JsonTypeInfo, which does automatic embeddeding (when writing) and use of type information.
There are multiple kinds of type info (class name, logical type name), as well as inclusion mechanisms (as-included-property, as-wrapper-array, as-wrapper-object). This page: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization explains some of the concepts.
A full fledged answer with a very clear example can be found here: https://stackoverflow.com/a/30386694/584947
Jackson refers to this as Polymorphic Deserialization.
It definitely helped me with my issue. I had an abstract class that I was saving in a database and needed to unmarshal it to a concrete instance of a class (understandably).
It will show you how to properly annotate the parent abstract class and how to teach jackson how to pick among the available sub-class candidates at run-time when unmarshaling.
If you want to pollute neither your JSON with extra fields nor your classes with annotation, you can write a very simple module and deserializer that uses the default subclass you want. It is more than one line due to some boilerplate code, but it is still relatively simple.
class AnimalDeserializer extends StdDeserializer<Animal> {
public AnimalDeserializer() {
super(Animal.class);
}
public Animal deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
return jsonParser.readValueAs(Cat.class);
}
}
class AnimalModule extends SimpleModule {
{
addDeserializer(Animal.class, new AnimalDeserializer());
}
}
Then register this module for the ObjectMapper and that's it (Zoo is the container class that has an Animal field).
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new AnimalModule());
return objectMapper.readValue(json, Zoo.class);
The problem can be solved with the annotation #JsonDeserialize on the abstract class.
Refers to Jackson Exceptions Problems and Solutions for more info

Categories

Resources