[英]Gson serialize null for specific class or field
我想序列化特定字段或類的空值。
在 GSON 中,選項serializeNulls()
適用於整個 JSON。
例子:
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;
使用 GSON 創建 JSON:
GsonBuilder builder = new GsonBuilder().serializeNulls();
Gson gson = builder.create();
System.out.println(gson.toJson(mainClass));
當前輸出:
{
"id": "101",
"name": null,
"test": {
"name": "testName",
"value": null
}
}
期望的輸出:
{
"id": "101",
"test": {
"name": "testName",
"value": null
}
}
如何達到預期的輸出?
首選解決方案將具有以下屬性:
我有一個類似於 Aleksey 的解決方案,但它可以應用於任何類中的一個或多個字段(Kotlin 中的示例):
為應序列化為 null 的字段創建新注釋:
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
annotation class SerializeNull
創建一個TypeAdapterFactory
來檢查一個類是否具有使用此注解注解的字段,並在編寫對象時從JsonTree
中刪除為null
且未使用該注解注解的字段:
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)
}
}
}
}
向您的 Gson 實例注冊適配器:
val builder = GsonBuilder().registerTypeAdapterFactory(SerializableAsNullConverter())
並注釋您希望可以為空的字段:
class MyClass(val id: String?, @SerializeNull val name: String?)
序列化結果:
val myClass = MyClass(null, null)
val gson = builder.create()
val json = gson.toJson(myClass)
json:
{
"name": null
}
我有接口來檢查對象何時應序列化為空:
public interface JsonNullable {
boolean isJsonNull();
}
以及對應的TypeAdapter(支持只寫)
public class JsonNullableAdapter extends TypeAdapter<JsonNullable> {
final TypeAdapter<JsonElement> elementAdapter = new Gson().getAdapter(JsonElement.class);
final TypeAdapter<Object> objectAdapter = new Gson().getAdapter(Object.class);
@Override
public void write(JsonWriter out, JsonNullable value) throws IOException {
if (value == null || value.isJsonNull()) {
//if the writer was not allowed to write null values
//do it only for this field
if (!out.getSerializeNulls()) {
out.setSerializeNulls(true);
out.nullValue();
out.setSerializeNulls(false);
} else {
out.nullValue();
}
} else {
JsonElement tree = objectAdapter.toJsonTree(value);
elementAdapter.write(out, tree);
}
}
@Override
public JsonNullable read(JsonReader in) throws IOException {
return null;
}
}
按如下方式使用它:
public class Foo implements JsonNullable {
@Override
public boolean isJsonNull() {
// You decide
}
}
在 Foo 值應序列化為 null 的類中。 注意 foo 值本身不能為空,否則自定義適配器注解將被忽略。
public class Bar {
@JsonAdapter(JsonNullableAdapter.class)
public Foo foo = new Foo();
}
對於那些尋找 @Joris 出色答案的 Java 版本的人來說,下面的代碼應該可以解決問題。 它在很大程度上只是 Kotlin 的翻譯,對如何獲取屬性的序列化名稱進行了微小的改進,以確保它在序列化名稱與屬性名稱不同時始終有效(請參閱原始答案的注釋)。
這是TypeAdapterFactory
的實現:
public class NullableAdapterFactory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Field[] declaredFields = type.getRawType().getDeclaredFields();
List<String> nullableFieldNames = new ArrayList<>();
List<String> nonNullableFieldNames = new ArrayList<>();
for (Field declaredField : declaredFields) {
if (declaredField.isAnnotationPresent(JsonNullable.class)) {
if (declaredField.getAnnotation(SerializedName.class) != null) {
nullableFieldNames.add(declaredField.getAnnotation(SerializedName.class).value());
} else {
nullableFieldNames.add(declaredField.getName());
}
} else {
if (declaredField.getAnnotation(SerializedName.class) != null) {
nonNullableFieldNames.add(declaredField.getAnnotation(SerializedName.class).value());
} else {
nonNullableFieldNames.add(declaredField.getName());
}
}
}
if (nullableFieldNames.size() == 0) {
return null;
}
TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(NullableAdapterFactory.this, type);
TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
JsonObject jsonObject = delegateAdapter.toJsonTree(value).getAsJsonObject();
for (String name: nonNullableFieldNames) {
if (jsonObject.has(name) && jsonObject.get(name) instanceof JsonNull) {
jsonObject.remove(name);
}
}
boolean originalSerializeNulls = out.getSerializeNulls();
out.setSerializeNulls(true);
elementAdapter.write(out, jsonObject);
out.setSerializeNulls(originalSerializeNulls);
}
@Override
public T read(JsonReader in) throws IOException {
return delegateAdapter.read(in);
}
};
}
}
這是標記目標屬性的@JsonNullable
注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JsonNullable {
}
我將它實現為對象類上的@JsonAdapter(NullableAdapterFactory.class)
注釋,而不是將其注冊為TypeAdapterFactory
實例上的GsonBuilder
,所以我的對象類看起來有點像這樣:
@JsonAdapter(NullableAdapterFactory.class)
public class Person {
public String firstName;
public String lastName;
@JsonNullable
public String someNullableInfo;
}
但是,如果願意,另一種方法應該同樣適用於此代碼。
創建com.google.gson.TypeAdapter
的子類並使用注釋com.google.gson.annotations.JsonAdapter
將其注冊為必填字段。 或者使用GsonBuilder.registerTypeAdapter
注冊它。 在那個適配器中write
(和read
)應該被實現。 例如:
public class JsonTestNullableAdapter extends TypeAdapter<Test> {
@Override
public void write(JsonWriter out, Test value) throws IOException {
out.beginObject();
out.name("name");
out.value(value.name);
out.name("value");
if (value.value == null) {
out.setSerializeNulls(true);
out.nullValue();
out.setSerializeNulls(false);
} else {
out.value(value.value);
}
out.endObject();
}
@Override
public Test read(JsonReader in) throws IOException {
in.beginObject();
Test result = new Test();
in.nextName();
if (in.peek() != NULL) {
result.name = in.nextString();
} else {
in.nextNull();
}
in.nextName();
if (in.peek() != NULL) {
result.value = in.nextString();
} else {
in.nextNull();
}
in.endObject();
return result;
}
}
在MainClass
中將帶有適配器的JsonAdapter
注釋添加到Test
類字段:
public static class MClass {
public String id;
public String name;
@JsonAdapter(JsonTestNullableAdapter.class)
public Test test;
}
System.out.println(new Gson.toJson(mainClass))
的輸出是:
{
"id": "101",
"test": {
"name": "testName",
"value": null
}
}
我從這里的各種答案中獲得了一些想法。
這個實現:
JsonNullable.isJsonNull() == true
時發生JsonNullable.isJsonNull() == false
時發生JsonNullable
的Parent
中發生的字段為null
TypeAdapterFactory
將未處理的工作正確委托給delegateAdapter
可能需要序列化為 null 的對象實現此接口
/**
* [JsonNullableTypeAdapterFactory] needs to be registered with the [com.google.gson.Gson]
* serializing implementations of [JsonNullable] for [JsonNullable] to work.
*
* [JsonNullable] allows objects to choose at runtime whether they should be serialized as "null"
* serialized normally, or be omitted from the JSON output from [com.google.gson.Gson].
*
* when [isJsonNull] returns true, the subclass will be serialized to a [com.google.gson.JsonNull].
*
* when [isJsonNull] returns false, the subclass will be serialized normally.
*/
interface JsonNullable {
/**
* return true to have the entire object serialized as `null` during JSON serialization.
* return false to have this object serialized normally.
*/
fun isJsonNull(): Boolean
}
將值序列化為 null 的類型適配器工廠
class JsonNullableTypeAdapterFactory : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
return object : TypeAdapter<T>() {
private val delegateAdapter = gson.getDelegateAdapter(this@JsonNullableTypeAdapterFactory, type)
override fun read(reader: JsonReader): T = delegateAdapter.read(reader)
override fun write(writer: JsonWriter, value: T?) {
if (value is JsonNullable && value.isJsonNull()) {
val originalSerializeNulls = writer.serializeNulls
writer.serializeNulls = true
writer.nullValue()
writer.serializeNulls = originalSerializeNulls
} else {
delegateAdapter.write(writer, value)
}
}
}
}
}
向 GSON 注冊 thr 類型適配器工廠
new GsonBuilder()
// ....
.registerTypeAdapterFactory(new JsonNullableTypeAdapterFactory())
// ....
.create();
序列化為 JSON 的示例對象
data class Parent(
val hello: Child?,
val world: Child?
)
data class Child(
val name: String?
) : JsonNullable {
override fun isJsonNull(): Boolean = name == null
}
添加到@Arvoreniad 給出的答案
這兩個添加是在將輸出設置為 true 后重置 JsonWriter 中的空序列化狀態,並使用 Gson 的字段命名策略來獲取字段名稱。
public class SerializeNullTypeAdapterFactory implements TypeAdapterFactory {
/**
* {@inheritDoc}
*/
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Field[] declaredFields = type.getRawType().getDeclaredFields();
List<String> nullableFields = new ArrayList<>();
List<String> nonNullableFields = new ArrayList<>();
FieldNamingStrategy fieldNamingStrategy = gson.fieldNamingStrategy();
for (Field declaredField : declaredFields) {
// The Gson FieldNamingStrategy will handle the @SerializedName annotation + casing conversions
final String fieldName = fieldNamingStrategy.translateName(declaredField);
if (declaredField.isAnnotationPresent(JsonNullable.class)) {
nullableFields.add(fieldName);
} else {
nonNullableFields.add(fieldName);
}
}
if (nullableFields.isEmpty()) {
return null;
}
TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(this, type);
TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
JsonObject jsonObject = delegateAdapter.toJsonTree(value).getAsJsonObject();
nonNullableFields.forEach((var name) -> {
if (jsonObject.has(name) && (jsonObject.get(name) instanceof JsonNull)) {
jsonObject.remove(name);
}
});
boolean serializeNulls = out.getSerializeNulls();
out.setSerializeNulls(true);
elementAdapter.write(out, jsonObject);
// Reset default (in case JsonWriter is reused)
out.setSerializeNulls(serializeNulls);
}
@Override
public T read(JsonReader in) throws IOException {
return delegateAdapter.read(in);
}
};
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.