I want to serialize nulls for a specific field or class.
In GSON, the option serializeNulls()
applies to the whole JSON.
Example:
class MainClass { public String id; public String name; public Test test; } class Test { public String name; public String value; } MainClass mainClass = new MainClass(); mainClass.id = "101" // mainClass has no name. Test test = new Test(); test.name = "testName"; test.value = null; mainClass.test = test;
Creating JSON using GSON:
GsonBuilder builder = new GsonBuilder().serializeNulls(); Gson gson = builder.create(); System.out.println(gson.toJson(mainClass));
Current ouput:
{ "id": "101", "name": null, "test": { "name": "testName", "value": null } }
Desired output:
{ "id": "101", "test": { "name": "testName", "value": null } }
How to achieve the desired output?
Preferred solution would have the following properties:
We can force Gson to serialize null values via the GsonBuilder class. We need to call the serializeNulls() method on the GsonBuilder instance before creating the Gson object. Once serializeNulls() has been called the Gson instance created by the GsonBuilder can include null fields in the serialized JSON.
As you can see, Gson will ignore the unknown fields and simply match the fields that it's able to.
Nested Classes (including Inner Classes)Gson can serialize static nested classes quite easily.
I have a solution similar to the one of Aleksey but that can be applied to one or more fields in any class (example in Kotlin):
Create a new annotation for fields that should be serialized as null:
@Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FIELD) annotation class SerializeNull
Create a TypeAdapterFactory
that checks if a class has fields annotated with this annotation and removes the fields that are null
and not annotated with the annotation from the JsonTree
when writing the object:
class SerializableAsNullConverter : TypeAdapterFactory { override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? { fun Field.serializedName() = declaredAnnotations .filterIsInstance<SerializedName>() .firstOrNull()?.value ?: name val declaredFields = type.rawType.declaredFields val nullableFieldNames = declaredFields .filter { it.declaredAnnotations.filterIsInstance<SerializeNull>().isNotEmpty() } .map { it.serializedName() } val nonNullableFields = declaredFields.map { it.serializedName() } - nullableFieldNames return if (nullableFieldNames.isEmpty()) { null } else object : TypeAdapter<T>() { private val delegateAdapter = gson.getDelegateAdapter(this@SerializableAsNullConverter, type) private val elementAdapter = gson.getAdapter(JsonElement::class.java) override fun write(writer: JsonWriter, value: T?) { val jsonObject = delegateAdapter.toJsonTree(value).asJsonObject nonNullableFields .filter { jsonObject.get(it) is JsonNull } .forEach { jsonObject.remove(it) } val originalSerializeNulls = writer.serializeNulls writer.serializeNulls = true elementAdapter.write(writer, jsonObject) writer.serializeNulls = originalSerializeNulls } override fun read(reader: JsonReader): T { return delegateAdapter.read(reader) } } } }
Register the adapter with your Gson instance:
val builder = GsonBuilder().registerTypeAdapterFactory(SerializableAsNullConverter())
And annotate the fields you would like to be nullable:
class MyClass(val id: String?, @SerializeNull val name: String?)
Serialization result:
val myClass = MyClass(null, null) val gson = builder.create() val json = gson.toJson(myClass)
json:
{ "name": null }
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