Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
416 views
in Technique[技术] by (71.8m points)

java - Gson serialize null for specific class or field

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:

  • Do NOT serialize nulls by default,
  • Serialize nulls for fields with a specific annotation.
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...