I am converting a JSON into a java object. Several fields in my java object class use a custom serializer that I have defined. For example:
#JsonProperty("table_name")
#JsonSerialize(using = MyCustomSerializer.class)
private String tableName;
I only want to use my custom serializer in certain instances. Is there a way to set using = in the #JsonSerialize annotation?
I have the following JacksonAnnotationIntrospector in my ObjectMapper:
private static final JacksonAnnotationIntrospector ignoreCustomSerializer = new JacksonAnnotationIntrospector() {
#Override
protected <A extends Annotation> A _findAnnotation(final Annotated annotated, final Class<A> annoClass) {
if (!annotated.hasAnnotation(JsonSerialize.class)) {
return super._findAnnotation(annotated, annoClass);
}
return null;
}
};
objectMapper.setAnnotationIntrospector(ignoreCustomSerializer);
This introspector causes my object mapper to ignore all fields wit the #JsonSerialize annotation. I don't want to ignore the field, just the custom serializer I have set. Can the introspector be modified to change using = in the #JsonSerialize annotation?
Related
Let's suppose there is a class hierarchy in some third-party library. One of those classes has #JsonValue annotation. It results in serialization/deserialization of only one field. Is it possible to ignore #JsonValue annotation without subclassing and get all object fields serialized?
You can define a MixIn and override the #JsonValue via property or getter
- depending on where the annotation has been set in the third-party library.
Assuming we have a class Pojo with a #JsonValue annotated member prop:
class Pojo {
#JsonValue
private String prop;
private String prop2;
// getters and setters
}
We then define an abstract MixIn class like follows, overriding the particular member or method:
abstract class MixIn {
#JsonValue(false) // either annotating members
private String prop;
#JsonValue(false) // or annotating methods
abstract String getProp();
}
This can be added to the ObjectMapper like follows:
objectMapper.addMixIn(Pojo.class, MixIn.class);
Instead of just prop, now both prop and prop2 are serialized.
Is there a way to provide the Jackson Deserializer with a default value from "the outside" (e.g. DI container) that it will use when deserializing an object, in this case tagRegistry?
#JsonCreator
public ExtractionRule(#JsonProperty("id") String id,
TagRegistry tagRegistry) {
this.id = id;
this.tagRegistry = tagRegistry;
}
I couldn't find an easy way to do this.
You could try #JacksonInject. Add this member to the ExtractionRule class:
#JacksonInject("tagRegistry")
private TagRegistry tagRegistry;
And inject the tagRegistry to the ObjectMapper before deserialization:
InjectableValues.Std injectableValues = new InjectableValues.Std();
injectableValues.addValue("tagRegistry", tagRegistry);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setInjectableValues(injectableValues);
I haven't tried using it in a constructor, not sure if that works.
You can find further examples here:
https://www.logicbig.com/tutorials/misc/jackson/jackson-inject.html
https://www.concretepage.com/jackson-api/jackson-jacksoninject-example#JacksonInject
I have class with #JsonIgnore-d field:
public class MyClass {
...
#JsonIgnore
private SomeType myfield;
...
// getters & setters
}
Is it possible to configure ObjectWriter so that it includes myfield during serialization even though being ingored?
Rationale: MyClass is serialized in many places and only in single specific one I want to have myfield.
It is possible to configure ObjectMapper to disable a JsonIgnore function. Following are some possible solution you can try with:
1.
Disable JsonIgnore function for a particular annotated field.
You can create a custom JsonIgnore annotation and a custom JacksonAnnotationIntrospector to remove the annotation from mapper context.
Following are the ideas:
Annotate #MyJsonIgnore to the fields that should be ignored while serialization:
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class MyClass {
#MyJsonIgnore
private SomeType myField;
}
#MyJsonIgnore is a simple custom annotation that wrap #JsonIgnore:
#Retention(RetentionPolicy.RUNTIME)
#JacksonAnnotationsInside
#JsonIgnore
public #interface MyJsonIgnore {
}
A custom JacksonAnnotationIntrospector is implemented to remove #MyJsonIgnore from mapper context:
public class DisablingMyJsonIgnoreIntrospector extends JacksonAnnotationIntrospector {
#Override
public boolean isAnnotationBundle(final Annotation ann) {
if (ann.annotationType().equals(MyJsonIgnore.class)) {
return false;
} else {
return super.isAnnotationBundle(ann);
}
}
After that, you can set the introspector on a ObjectMapper during configuration:
ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new DisablingMyJsonIgnoreIntrospector());
It results that the fields annotated with #MyJsonIgnore can be marshaled properly.
2.
Disable JsonIgnore function for the mapper
Your can create a custom JacksonAnnotationIntrospector and override hasIgnoreMarker method to always return false:
public static class DisablingJsonIgnoreIntrospector extends JacksonAnnotationIntrospector {
#Override
public boolean hasIgnoreMarker(final AnnotatedMember m) {
return false;
}
}
hasIgnoreMarker is to check whether there is annotation to ignore json property. Return false will disable the JsonIngore function.
3.
Disable all annotations and specify what kinds of properties are auto-detected for a given ObjectMapper:
final ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.USE_ANNOTATIONS);
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
This simply disable all annotations.
Hope this can help.
One more option is to use the AnnotationIntrospector.nopInstance() if you want to avoid all Jackson's annotations in your pojo including #JsonIgnore e.g.
JsonMapper.builder().annotationIntrospector(AnnotationIntrospector.nopInstance()).build()...
or
new ObjectMapper().setAnnotationIntrospector(AnnotationIntrospector.nopInstance())...
public void serialize(IPerson person, OutputStream output) throws Exception {}
public void deserialize(InputStream input) throws Exception {}
I have an interface named IPerson, it has basic functionality.
I want to serialize the person object and be able to deserialize it from the deserialize method.
However, the scenario is this I cannot use Java's serializable interface as I can't be sure what implementation of IPerson will be used.
I have chosen to use Jackson's FasterXML, using ObjectMapper m = new ObjectMapper();
The problem I am having is that since IPerson is an interface I cannot serialize it directly using mapper.writerValue(output, person), I figured I must convert this object into something else, say a ByteArray then serialize it?
Also, this would be converting this something else into an object when deserializing? I have minimal experience with what exactly I should convert this object to and how to do so? Any ideas?
When using the default ObjectMapper you will have to make sure the objects you serialize are Java Beans. For non-bean classes you can set field visibility using m.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); or annotate your class using #JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY).
For deserializing you will have to tell the ObjectMapper the target type. This can be done by providing a concrete implementation type to readValue or by storing the classname within the exported JSON. For this you can set m.enableDefaultTypingAsProperty(DefaultTyping.OBJECT_AND_NON_CONCRETE, "__class"); and annotate your objects with #JsonTypeInfo
ObjectMapper om = new ObjectMapper();
om.enableDefaultTypingAsProperty(DefaultTyping.OBJECT_AND_NON_CONCRETE, "__class");
IPerson value = new MyPerson();
String s = om.writeValueAsString(value);
IPerson d = om.readValue(s, IPerson.class);
using
interface IPerson {
void doSomething();
}
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "__class")
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
class MyPerson implements IPerson {
String name;
#Override
public void doSomething() {
}
}
Note that, you will need a default constructor for this to work or work with #JsonCreator and #JsonProperty (see jackson-annotations for details)
Suppose you have this entity:
class Foo{
String propA;
String propB;
}
and you want to serialize for one API like :
{propA: "ola",
propB: "Holla"}
and for another API like :
{fooPropA: "ola",
fooPropB: "Holla"}
How can this be achieved using jackson and using the same entity. Creating 2 different entities is not an option :)
There are several ways in which you can achieve this. You can enable a custom serializer (already covered by #se_vedem), register an annotation introspector which changes the property names for the corresponding class and so on.
However, if you are willing to only add a string prefix to all the property names, then the Jackson property name strategy is probably the best fit. The naming strategy class has the access to the serialized object type information, so you can make a decision whether to change the property name or not.
Here is an example using a custom annotation that defines the prefix:
public class JacksonNameStrategy {
#Retention(RetentionPolicy.RUNTIME)
public static #interface PropertyPrefix {
String value();
}
#PropertyPrefix("foo_")
public static class Foo {
public String propA;
public String propB;
public Foo(String propA, String propB) {
this.propA = propA;
this.propB = propB;
}
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(new MyPropertyNamingStrategyBase());
System.out.println(mapper.writeValueAsString(new Foo("old", "Holla")));
}
private static class MyPropertyNamingStrategyBase extends PropertyNamingStrategy {
#Override
public String nameForField(MapperConfig<?> config,
AnnotatedField field,
String defaultName) {
PropertyPrefix ann = field.getDeclaringClass().getAnnotation(PropertyPrefix.class);
if (ann != null) {
return ann.value() + defaultName;
}
return super.nameForField(config, field, defaultName);
}
}
}
Output:
{"foo_propA":"old","foo_propB":"Holla"}
In your API method you choose between two ObjectMapper instances one with the default naming naming strategy and one with the custom one.
You can achieve this by using modules feature from Jackson.
Basically, each API would have it's own ObjectMapper and they will be configured with different modules. This way you can create 2 serializers for the same class and register them on the appropriate module. More read can be found here http://wiki.fasterxml.com/JacksonFeatureModules
However, be aware that serializers are loaded in a particular order. First it tries to get the annotated ones, if none is found it will try to get those registered from modules. So, for example if you have your class annotated with serializer, then that serializer(FooSerializer) would be chosen instead of the one configured in module(MySecondFooSerializer).
#JsonSerialize(using = FooSerializer.class)
class Foo{
String propA;
String propB;
}
module.addSerializer(Foo.class, new MySecondFooSerializer());