Gson序列化特定类或字段的null

Mar*_*tin 16 java json gson

我想序列化特定字段或类的空值.

在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;    
Run Code Online (Sandbox Code Playgroud)

使用GSON创建JSON:

GsonBuilder builder = new GsonBuilder().serializeNulls();
Gson gson = builder.create();
System.out.println(gson.toJson(mainClass));
Run Code Online (Sandbox Code Playgroud)

当前输出:

{
    "id": "101",
    "name": null,
    "test": {
        "name": "testName",
        "value": null
    }
}
Run Code Online (Sandbox Code Playgroud)

期望的输出:

{
    "id": "101",
    "test": {
        "name": "testName",
        "value": null
    }
}
Run Code Online (Sandbox Code Playgroud)

如何实现所需的输出?

首选解决方案具有以下属性:

  • 千万不要用默认的序列空,
  • 序列化具有特定注释的字段的空值.

Jor*_*ris 12

我有一个类似于 Aleksey 的解决方案,但它可以应用于任何类中的一个或多个字段(Kotlin 中的示例):

为应序列化为 null 的字段创建一个新注释:

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
annotation class SerializeNull
Run Code Online (Sandbox Code Playgroud)

创建一个TypeAdapterFactory检查类是否具有使用此批注注释的字段,nullJsonTree在编写对象时删除使用该批注和未使用该批注进行批注的字段:

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
annotation class SerializeNull
Run Code Online (Sandbox Code Playgroud)

向您的 Gson 实例注册适配器:

val builder = GsonBuilder().registerTypeAdapterFactory(SerializableAsNullConverter())
Run Code Online (Sandbox Code Playgroud)

并注释您希望可以为空的字段:

class MyClass(val id: String?, @SerializeNull val name: String?)
Run Code Online (Sandbox Code Playgroud)

序列化结果:

val myClass = MyClass(null, null)
val gson = builder.create()
val json = gson.toJson(myClass)
Run Code Online (Sandbox Code Playgroud)

json:

{
    "name": null
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你!我使用 Retrofit 在 Java 项目中重新实现了它,这是我在 GSON 中找到的可自定义可为空类型的最佳解决方案。请注意,至少在 Java 中,您需要从“declaredField.getAnnotation(SerializedName.class).value()”中提取序列化字段名称,以便使其适用于更复杂的变量名称。 (2认同)

Ale*_*sny 6

我有接口来检查对象何时应该被序列化为空:

public interface JsonNullable {
  boolean isJsonNull();
}
Run Code Online (Sandbox Code Playgroud)

以及对应的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;
  }
}
Run Code Online (Sandbox Code Playgroud)

使用方法如下:

public class Foo implements JsonNullable {
  @Override
  public boolean isJsonNull() {
    // You decide
  }
}
Run Code Online (Sandbox Code Playgroud)

在 Foo 值应序列化为 null 的类中。注意 foo 值本身不能为空,否则自定义适配器注解将被忽略。

public class Bar {
  @JsonAdapter(JsonNullableAdapter.class)
  public Foo foo = new Foo();
}
Run Code Online (Sandbox Code Playgroud)


Arv*_*iad 5

对于那些正在寻找@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);
                    }
                }

                out.setSerializeNulls(true);
                elementAdapter.write(out, jsonObject);
            }

            @Override
            public T read(JsonReader in) throws IOException {
                return delegateAdapter.read(in);
            }

        };
    }
}
Run Code Online (Sandbox Code Playgroud)

这是@JsonNullable标记目标属性的注释:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JsonNullable {
}
Run Code Online (Sandbox Code Playgroud)

我将它实现为@JsonAdapter(NullableAdapterFactory.class)对象类上的注释,而不是将其注册为实例TypeAdapterFactory上的a GsonBuilder,所以我的对象类看起来有点像这样:

@JsonAdapter(NullableAdapterFactory.class)
public class Person {
  public String firstName;
  public String lastName;

  @JsonNullable
  public String someNullableInfo;
}

Run Code Online (Sandbox Code Playgroud)

但是,如果愿意,另一种方法应该与此代码同样有效。