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;
}
}
Related
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.
I have a POJO with a field or property, containing collection of objects, something like this:
public class Box {
public List<Items> items;
}
By default, value of items is null, and I do not want to initialize it with empty list.
Now, if I try to serialize it with Jackson, I get NullPointerException. Is there a simple way to make Jackson not break on such value and serialize it as an empty collection: [ ]?
Note. This class is just a simplified example. In reality, there are a hundred of classes and a number of fields with different names in each of them, which are occasionally set to null sometimes somewhere in the code, breaking serialization in runtime.
If you do not want to change the contract of your POJO class, think about the possibility to define custom Jackson serializer / deserializer which extend JsonSerializer<T> and JsonDeserializer<T> respectively. E.g.:
public class CountryDeserializer extends JsonDeserializer<CountryCode> {
#Override
public CountryCode deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException {
return CountryCode.getByCode(jp.getText());
}
}
and then
#JsonDeserialize(using=CountryDeserializer.class)
private CountryCode country;
You can check whether your field is null and act accordingly, in both directions (serialization / deserialization).
Have you considered making this class a JavaBean?
In that case, you would be able to give a default value in the getter:
public class Box {
private List<Items> items;
public List<Items> getItems() {
if(null == items) {
return Collections.emptyList();
}
return this.items;
}
//Setter here
}
This approach would prevent a lot of trouble related to Jackson's assumptions.
Update: Based on clarification... You could implement a custom serializer for the list type (and/or any other desired customization). Please note that :
public class ListSerializer extends JsonSerializer<List> {
#Override
public void serialize(List value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
JsonProcessingException {
if (null == value) {
provider.defaultSerializeValue(new ArrayList<Object>(), jgen);
} else {
provider.defaultSerializeValue(value, jgen);
}
}
}
//Then your code could set the serializer on the object mapper as follows:
objectMapper.addSerializer(List.class, new ListSerializer());
Repeat for all such customization.
Code was inspired by this article: http://www.baeldung.com/jackson-custom-serialization
I'm serializing some existing objects with Jackson 2.22, leveragin the MixIn feature to decouple the real object from the Jackson annotations configuration.
Actually my mixin is an interface that declares the same methods of the target class and annotates them, here's an example.
Target class:
public class Product {
// ...
public String getName();
public String getDescription();
public String getPrice();
public String getFinalPrice();
public String getDiscount();
// ...
}
and the mixin:
public interface ProductApi {
#JsonProperty
public String getName();
#JsonProperty("price")
public String getFinalPrice();
}
My JSON should have some more informations, computed from several methods or fields of the target class.
Is this even possible in Jackson?
I tried turning the mixin in a class and adding a new method there, but that didn't work.
public class ProductApi {
#JsonProperty
public String getName();
#JsonProperty("price")
public String getFinalPrice();
#JsonProperty("images")
public List<String> getImages() { /* ... */ }
}
I guess this is because the mixin only provides annotations for the target class, but is the latter that is read for serialization.
Of course, if I change the object to be serialized with a new subclass that contains the new method I need, that works, but the objects come from our services layers, and this would mean I have to rewrite all those methods.
I'm using Jackson with Jersey, so don't want to change Jackson with another library.
Here's how I did it.
The solution is to specify a custom JsonSerializer implementation to the field getter.
First of all, I changed the mixin interface to a class that extends the entity (target) class, so that it can access the target class data.
public class ProductApi extends Product {
#JsonProperty
#Override
public String getName() {
return super.getName();
};
// ...
}
Next, I implemented the JsonSerializer that would create the derived property I want:
public static class ImagesSerializer extends JsonSerializer<String> {
#Override
public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
Product p = (Product) jgen.getCurrentValue();
int num = p.getNumberOfImages();
List<String> imgs = new ArrayList<String>(num);
for(int i = 0; i < num; i++) {
String src = "/include/images/showImage.jsp?"+"id="+p.getId()+"&number="+i;
imgs.add(src);
}
provider.defaultSerializeValue(imgs, jgen);
}
}
This is a really simple implementation, more safety checks should be done.
What this does is, basically, retrieve the whole entity instance from the JSON generator, build up a custom object and then ask Jackson to serialize it.
I implemented it inside my ProductApi as a static class, but just for simplicity.
Finally, the serializer needs to be bound to the JsonProperty annotated field:
public class ProductApi extends Product {
#JsonProperty
#Override
public String getName() {
return super.getName();
};
// ...
#JsonSerialize(using=ImagesSerializer.class)
#JsonProperty("images")
#Override
public String getImage() { // in my entity this returns an image number, whereas in my JSON I want a list of URLs
return "";
}
// ...
}
As a side note, it seems that the returned value of the getImage() method is not used.
Why don't you just make some fields, which should be serialized and use Gson for it?
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.
I have the following use case, lets say I have an object CAR with a list of WHEEL objects
class Car{
List<Wheel> wheels;
//getters and setters..
}
Now lets say that the Wheel class have a property boolean isWinterTyre;
I would like to know if there is any annotation, custom serialization stuff maybe or something else, to annotate somehow a Car class or the Wheel class itself to exclude all wheel objects having property isWinterTyre = true contained in 'wheels' list while serializing my Java 'Car' instance to JSON representation.
Should be possible using custom serializes. See tutorial here.
#JsonSerialize(using = CustomSerializer.class)
class Wheel {
//getters and setters..
}
public class CustomSerializer extends JsonSerializer<Wheel> {
#Override
public void serialize(Wheel value, JsonGenerator generator,
SerializerProvider provider) throws IOException,
JsonProcessingException {
if(value.getSomething() == something) {
//Output Wheel using the generator
} else {
//skip it
}
}
}