2017-03-01 49 views
4

GSON系列化:排除給定一個POJO某些字段如果爲空,但不是所有的人都

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

我想要的「手機」在任何時候都被序列化,但「傳真」,只有當它不爲空。因此,考慮一個Person 「約翰」,沒有電話或傳真:

電流:

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

我需要什麼:

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

有什麼樣:

@Expose (serialize_if_null = false) 

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

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

感謝

回答

1

使用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); 
    } 
} 

這種行爲無法改變,但我相信這是一個很好的設計選擇來隔離應用程序對象及其序列化表示(請參閱Data Transfer Object模式),以便不混合概念並使應用程序組件鬆散耦合(有朝一日從Gson遷移只會對相應的DTO類進行修改)。

如果你的罰款與具有被引入到您的應用程序的DTO,那麼你可以爲這兩種情況下創建單獨的DTO類:保留phone並根據fax字段值丟棄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; 
    } 

} 

我真的不知道,但我認爲,對於系列化目的創建JSON樹,而不是使用DTO的可能(至少是因爲更復雜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)); 

這兩種解決方案輸出:

{ 「名」: 「約翰」, 「手機」:空}

+0

upvoted。 「Person」對象只是一個簡單的例子,所以DTO風格的解決方案可能不是最乾淨的方式...可能類似personbuilder()。withfax()。build()方法? – sikidhart

+0

@sikidhart好吧,這取決於,我會用DTO去減少容易出錯的地方。注意這個技巧:當調用'toJson'方法時,Gson可以根據對象的實際類型區分'PersonDto'和'PersonDtoWithFax'。您提到的構建器模式只是創建DTO的另一種方式(我僅使用構造器僅用於答案簡單目的)。如果您想使用構建器,這完全取決於您,並且可以使用'withFax(...)'返回一個PersonDtoWithFaxBuilder實例來輕鬆創建自己的構建器。但引進建設者通常會帶來一些複雜性。你決定。 –

相關問題