I'm using gson to serialize some object. I have a requirement that any null field should be treated as an empty string, independent on the variable being a string, double or a pojo.
I tried to create a custom serializer for Object and simply return a new JsonPrimitive("") for the null valued objects, but the problem is how to handle the non-null valued objects without the use of "instanceof" or "getClass" and handling every single type.
Any thoughts on how to do this is appreciated.
This can be done using a custom TypeAdaptor for your model Object.
You can iterate over the object field using reflection and whenever a field is null, set the value in the json representation to an empty string whenever you cross a null field.
This would absolutely be hard to maintain and should be done with some risks as @Sotirios Delimanolis stated, What if the corresponding reference is not a String, how are you going to intending to handle it back and forth?
public class MyEntity
{
private int id;
private String name;
private Long socialNumber;
private MyInnerEntity innerEntity;
public MyEntity(int id, String name, Long socialNumber, MyInnerEntity innerEntity)
{
this.id = id;
this.name = name;
this.socialNumber = socialNumber;
this.innerEntity = innerEntity;
}
public int getId()
{
return id;
}
public String getName()
{
return name;
}
public Long getSocialNumber()
{
return socialNumber;
}
public MyInnerEntity getInnerEntity()
{
return innerEntity;
}
public static class MyInnerEntity {
@Override
public String toString()
{
return "whateverValue";
}
}
}
TypeAdapter implementation which set any null value to and empty "" String:public class GenericAdapter extends TypeAdapter<Object>
{
@Override
public void write(JsonWriter jsonWriter, Object o) throws IOException
{
jsonWriter.beginObject();
for (Field field : o.getClass().getDeclaredFields())
{
Object fieldValue = runGetter(field, o);
jsonWriter.name(field.getName());
if (fieldValue == null)
{
jsonWriter.value("");
}
else {
jsonWriter.value(fieldValue.toString());
}
}
jsonWriter.endObject();
}
@Override
public Object read(JsonReader jsonReader) throws IOException
{
/* Don't forget to add implementation here to have your Object back alive :) */
return null;
}
/**
* A generic field accessor runner.
* Run the right getter on the field to get its value.
* @param field
* @param o {@code Object}
* @return
*/
public static Object runGetter(Field field, Object o)
{
// MZ: Find the correct method
for (Method method : o.getClass().getMethods())
{
if ((method.getName().startsWith("get")) && (method.getName().length() == (field.getName().length() + 3)))
{
if (method.getName().toLowerCase().endsWith(field.getName().toLowerCase()))
{
try
{
return method.invoke(o);
}
catch (IllegalAccessException e)
{ }
catch (InvocationTargetException e)
{ }
}
}
}
return null;
}
}
main method to add the adapter to Gson:public class Test
{
public static void main(String[] args)
{
Gson gson = new GsonBuilder().registerTypeAdapter(MyEntity.class, new GenericAdapter()).create();
Object entity = new MyEntity(16, "entity", (long)123, new MyEntity.MyInnerEntity());
String json = gson.toJson(entity);
System.out.println(json);
Object entity2 = new MyEntity(0, null, null, null);
json = gson.toJson(entity2);
System.out.println(json);
}
}
This would result in below output:
{"id":"16","name":"entity","socialNumber":"123","innerEntity":"whateverValue"}
{"id":"0","name":"","socialNumber":"","innerEntity":""}
Note that whatever the object is of type, its value is set to "" in the marshalled json string.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With