簡體   English   中英

Gson 序列化:如果 null 但不是全部,則排除某些字段

[英]Gson serialization: exclude SOME fields if null but not all of them

給定一個 POJO:

class Person {
    @Expose
    String name;
    @Expose
    String phone;
    @Expose
    String fax;
}

我希望“電話”始終被序列化,但只有當它不是 null 時才“傳真”。 所以給定一個沒有電話或傳真的人“約翰”:

當前的:

{ "name": "John", "phone": null, "fax": null }

我需要的:

{ "name": "John", "phone": null }

有沒有類似的東西:

@Expose (serialize_if_null = false)

瞬態不起作用,因為如果它有值,我仍然希望它序列化。

使用 ExclusionStrategy,我可以定義字段,但我似乎找不到獲取值的方法。

謝謝

使用ExclusionStrategy,我可以定義字段,但我似乎找不到獲取值的方法。

是的,它沒有提供確定當前字段值的方法。 這是因為Gson ReflectiveTypeAdapterFactory如何在內部工作( BoundField.serializedfinal ,只解析一次):

@Override public boolean writeField(Object value) throws IOException, IllegalAccessException {
  if (!serialized) return false;
  Object fieldValue = field.get(value);
  return fieldValue != value; // avoid recursion for example for Throwable.cause
}
for (BoundField boundField : boundFields.values()) {
  if (boundField.writeField(value)) {
    out.name(boundField.name);
    boundField.write(out, value);
  }
}

這種行為無法改變,但我認為將應用程序對象及其序列化表示(請參閱數據傳輸對象模式)分開是一個很好的設計選擇,以免混合概念並使應用程序組件松散耦合(有一天從Gson遷移只需要僅修改相應的DTO類)。

如果您將DTO引入應用程序就可以了,那么您可以為這兩種方案創建單獨的DTO類:根據fax字段值保留phone和丟棄fax

class PersonDto {
    @Expose String name;
    @Expose String phone;
    PersonDto(final Person person) {
        name = person.name;
        phone = person.phone;
    }
}
class PersonDtoWithFax extends PersonDto {
    @Expose String fax;
    PersonDtoWithFax(final Person person) {
        super(person);
        fax = person.fax;
    }
}

在這種情況下,序列化是直截了當的:

final Gson gson = new GsonBuilder()
        .serializeNulls()
        .create();
final Person person = new Person();
person.name = "John";
final PersonDto personDto = person.fax == null
        ? new PersonDto(person)
        : new PersonDtoWithFax(person);
System.out.println(gson.toJson(personDto));

如果你不想在這里引入隔離的DTO概念,你可能想要實現一個在實現上稍微復雜一點的自定義序列化程序,並且可能由於屬性名稱硬編碼而有些容易出錯(但是你可以有很好的測試,當然,或者從java.lang.reflect.Field實例中提取名稱。

final class SpecialJsonSerializer<T>
        implements JsonSerializer<T> {

    private final Gson gson; // Unfortunately, Gson does not provide much from JsonSerialiationContext, so we have to get it ourselves
    private final Iterable<String> excludeIfNull;

    private SpecialJsonSerializer(final Gson gson, final Iterable<String> excludeIfNull) {
        this.gson = gson;
        this.excludeIfNull = excludeIfNull;
    }

    static <T> JsonSerializer<T> getSpecialJsonSerializer(final Gson gson, final Iterable<String> excludeIfNull) {
        return new SpecialJsonSerializer<>(gson, excludeIfNull);
    }

    @Override
    public JsonElement serialize(final T object, final Type type, final JsonSerializationContext context) {
        // context.serialize(person, type) cannot work due to infinite recursive serialization
        // therefore the backing Gson instance is used
        final JsonObject jsonObject = gson.toJsonTree(object, type).getAsJsonObject();
        for ( final String propertyName : excludeIfNull ) {
            final JsonElement property = jsonObject.get(propertyName);
            if ( property != null && property.isJsonNull() ) {
                jsonObject.remove(propertyName);
            }
        }
        return jsonObject;
    }

}

我不太確定,但我認為從內存消耗的角度來看,創建用於序列化而不是使用DTO的JSON樹可能會稍微昂貴一些(至少因為更復雜的JsonElement結構)。

// Both Gson instances must have serializeNulls()
final Gson gson = new GsonBuilder()
        .serializeNulls()
        .create();
final Gson gsonWrapper = new GsonBuilder()
        .serializeNulls()
        .registerTypeAdapter(Person.class, getSpecialJsonSerializer(gson, singletonList("fax")))
        .create();
final Person person = new Person();
person.name = "John";
System.out.println(gsonWrapper.toJson(person));

兩種解決方案輸出

{ “名”: “約翰”, “手機”:空}

Yo lo he conseguido con éxito con un:

@Expose
@JsonInclude(Include.NON_NULL)
private String zona;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM