Jackson custom JsonSerializer - conditionally call default serializer - java

What I want is to use default BeanSerializer conditionally for my class's objects:
class MyCustomSerializer extends StdSerializer<AbstractEntity> {
public MyCustomSerializer() {
super(AbstractEntity.class);
}
#Override
public void serialize(AbstractEntity o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (someCondition()) {
serializeNormalWay(); //how?
} else {
//custom serialization
}
}
}
I've tried to do something like that:
serializerProvider.defaultSerializeValue(o, jsonGenerator);
but this calls MyCustomSerializer's method and I have never-ending recursion.
How can I get appropriate Serializer object, that I could use for ordinary bean Serialization?

This requires bit more complicated setup: instead of directly overriding serializer to use, you need to let Jackson create one, then take over.
This may be done by registering BeanSerializerModifier (via Module), method modifySerializer(...). You will be given default serializer that would be used, and you can construct custom one, passing that default one.

Related

Custom jackson serializer for all classes

I am trying to create a custom jackson serializer that will be applied to all classes if a certain criteria has been met. If not I would like to use the default serializer for that class.
Does anyone know if this is possible and how. Can I make the serializer for Object like this public class ObjectSerializer extends StdSerializer<Object> { and then just put #JsonSerialize(using = ObjectSerializer.class) on all classes?
Still, even if that's possible I don't know how to invoke default serializer for the concrete class if the condition hasn't been met
it is possible to create a custom Jackson serializer for all classes and then use the default serializer for the specific class if a certain condition has not been met.
public class ObjectSerializer extends StdSerializer<Object> {
public ObjectSerializer() {
this(null);
}
protected ObjectSerializer(Class<Object> t) {
super(t);
}
#Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
if (conditionMet(value)) {
// Custom serialization logic here
} else {
// Delegate to the default serializer for the concrete class
provider.defaultSerializeValue(value, jgen);
}
}
private boolean conditionMet(Object value) {
// Implementation of the condition check here
return false;
}
}

Custom annotation based ignore field from serialisation

Consider a case that I have 2 instance of Object Mapper.
I want one must exclude fields that are annotated with some custom annotation from serialization
While other mapper includes(ignores annotation)
Like class has 3 fields a,b,c and c is annotated with some annotation (say #IgnoreField)
(Their will n number of class, each will have their Fields that are not meant to be serialized)
Now 1st object mapper o1 must serialize only a and b.
While 2nd object mapper o2 can serialize a,b and c.
This can happen with any class having different fields some of which may be annotated.
You can always implement a custom JsonSerializer and register it with your ObjectMapper.
class Bean {
#Ignore
String a;
String b;
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#interface Ignore {
}
class BeanWithIgnoredFieldsSerializer extends JsonSerializer<Bean> {
#Override
public void serialize(final Bean value, final JsonGenerator gen, final SerializerProvider serializers) throws IOException, JsonProcessingException {
gen.writeStartObject();
try {
for (final Field f : Bean.class.getFields()) {
if (f.isAnnotationPresent(Ignore.class)) {
gen.writeStringField(f.getName(), (String) f.get(value));
}
}
} catch (final Exception e) {
//
}
gen.writeEndObject();
}
}
class BeanModule extends SimpleModule {
BeanModule() {
addSerializer(Bean.class, new BeanWithIgnoredFieldsSerializer());
}
}
void configure(final ObjectMapper om) {
om.registerModule(new BeanModule());
}
Note I have not tested this code, but that is the general idea how you add custom serializers to the OM. Adjust the code within the serialize method however you want.
Try configure SimpleBeanPropertyFilter for different condition.
#JsonFilter("someBeanFilter")
public class SomeBean {
}
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("someBeanFilter",SimpleBeanPropertyFilter.serializeAllExcept("aFild"));
ObjectMapper mapper = new ObjectMapper();
mapper.setFilterProvider(filterProvider);
A distinct non-answer:
This is most likely a terrible idea.
You write code to communicate your intention. When you use that annotation, then you are telling "everybody" that these fields should be ignored.
A human reader looking at your code might spend half a day asking himself later "it says #IgnoreField for a and c , so why the heck are a, and c showing up serialized data?"
In other words, whatever problem you are trying to solve here, the answer is most likely not by hacking your way into ignoring annotations sometimes.
The next best "reasonable" solution might be: to rely on different custom annotations, like #IgnoreAlways and something like #OnlyIncludeForXyz. In other words: clearly express what might happen. Instead of using declarative programming, to then "lie" about what you declared.

replace field name using #JsonTypeInfo

I would like to know if there is a way to replace the fieldname using #JsonTypeInfo
Here is what I want to achieve
class Tnode<T>{
#JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT, property="type")
T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
I get output as
{
"obj": {
"Foo": {
"name": "xyz"
}
}
}
The whole point is I do not want an extra layer "obj" as field name. I want the "Foo" to be one level above. In the code I am setting generic type to a concrete type. I want concrete class name to show up rather than having it wrapped.
I did try changing to include=As.PROPERTY but it will stil output as "obj".
I did solve using custom serializer. But I have to set every field.
public class CustomSerializer extends JsonSerializer<Object> {
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeObjectField("somename", value);
jgen.writeEndObject();
}
}
But the problem is when ever I have to add a instance variable in Tnode class I have to add that code in the custom serializer. And I want to avoid that.
Any suggestions?
There is no way to do that. Name of the property to contain Object, wrapped in type information, has to be statically known (to locate logical property). It can not vary.

Jackson How to retrieve parent bean in a custom Serializer/Deserializer

In a custom serializer/deserializer, is there a way to retrieve the parent bean of the field?
For example:
public class Foo {
#JsonSerialize(using = MyCustomSerializer.class)
public Bar bar;
}
public class Bar { }
public class MyCustomSerializer extends JsonSerializer<Bar> {
#Override
public void serialize(
Bar value,
JsonGenerator jgen,
SerializerProvider serializers)
throws IOException, JsonProcessingException
{
// get Foo ??
}
}
Here I'd like to get Foo in my serializer without having to have a reference inside Bar.
If you are using Jackson 2.5, it is possible to access parent object via JsonGenerator.getCurrentValue(). Or, further up the hierarchy, going via getOutputContext() (which has getParent() as well as getCurrentValue() method).
This is also available through JsonParser for custom deserializer.
For deserialization, where you don't have access to the JsonGenerator object. The following worked for me:
JsonStreamContext parsingContext = jsonParser.getParsingContext();
JsonStreamContext parent = parsingContext.getParent();
Object currentValue = parent.getCurrentValue();
Note: getCurrentValue will be null if you are using custom serialization
I worked around this by setting the parent object into the child's serializer instance and then accessing it when the child's serializer was called by jackson.

Serialize one class in two different ways with Jackson

In one of our projects we use a java webapp talking to a MongoDB instance. In the database, we use DBRefs to keep track of some object relations. We (de)serialize with POJO objects using jackson (using mongodb-jackson-mapper).
However, we use the same POJOs to then (de)serialize to the outside world, where our front end deals with presenting the JSON.
Now, we need a way for the serialization for the outside world to contain the referenced object from a DBRef (so that the UI can present the full object), while we obviously want to have the DBRef written to the database, and not the whole object.
Right now I wrote some untested static nested class code:
public static class FooReference {
public DBRef<Foo> foo;
// FIXME how to ensure that this doesn't go into the database?
public Foo getFoo() {
return foo.fetch();
}
}
Ideally I would like a way to annotate this so that I could (de)serialize it either with or without the getFoo() result, probably depending on some configuration object. Is this possible? Do you see a better way of going about doing this?
From looking at options, it seems you can annotate properties to only be shown if a given View is passed to the ObjectMapper used for serialization. You could thus edit the class:
public static class FooReference {
public DBRef<Foo> foo;
#JsonView(Views.WebView.class)
public Foo getFoo() {
return foo.fetch();
}
}
and provide:
class Views {
static class WebView { }
}
and then serialize after creating a configuration with the correct view:
SerializationConfig conf = objectMapper.getSerializationConfig().withView(Views.WebView.class);
objectMapper.setSerializationConfig(conf);
Which would then serialize it. Not specifying the view when serializing with the MongoDB wrapper would mean the method would be ignored. Properties without a JsonView annotation are serialized by default, a behaviour you can change by specifying:
objectMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
More info is available on the Jackson Wiki.
There are still other alternatives, too, it turns out: there are Jackson MixIns which would let you override (de)serialization behaviour of parts of a class without modifying the class itself, and as of Jackson 2.0 (very recent release) there are filters, too.
Use a custom JSONSerializer and apply your logic in the serialize method:
public static class FooReference {
public DBRef<Foo> foo;
#JsonSerialize(using = CustomSerializer.class)
public Foo getFoo() {
return foo.fetch();
}
}
public class CustomSerializer extends JsonSerializer<Object> {
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
// jgen.writeObjectField ...
}
}

Categories