2017-03-01 3 views
4

場合はnullすべてではなく、それらのいくつかのフィールドを除外する:それがnullでない場合にのみ、Gsonのシリアライズ:POJOを考えると

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

私はすべての回で連載される「電話」が、「ファックス」をしたいです。だから、電話やファックスなしで人 "ジョン" を与えられた:

電流:私は必要なもの

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

@Expose (serialize_if_null = false) 

過渡:

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

のようなものがあります私はそれが値を持っている場合、それがシリアル化されたままにしたいので、動作しません。

ExclusionStrategyを使用すると、フィールドを定義できますが、値を取得する方法が見つからないようです。 ExclusionStrategyを使用して

おかげ

答えて

1

は、私がフィールドを定義することができますが、私は値を取得する方法を見つけるように見えることはできません。

はい、現在のフィールド値を判断する方法はありません。これは、内部的に動作ReflectiveTypeAdapterFactory方法Gson(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クラスに対してのみ変更されます)。 phoneを維持し、faxフィールドの値に応じて、faxを捨てる:あなたは、あなたが両方のシナリオに別々のDTOクラスを作成することができ、アプリケーションに導入されたのDTOを有する微なら

。この場合

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)); 

両溶液出力:

{ "名前": "ジョン"、 "電話":NULL} upvoted

+0

。 "Person"オブジェクトは単純な例に過ぎないので、DTOスタイルの解決策は、最もきれいな方法ではないかもしれません。おそらくpersonbuilder()。withfax()。build()something ?? – sikidhart

+0

@sikidhartさて、それは依存していますが、私はDTOを使ってエラーを起こしにくくします。このトリックに注意してください。Gsonは、 'toJson'メソッドを呼び出すときに、オブジェクトの実際の型に対して' PersonDto'と 'PersonDtoWithFax'を区別できます。あなたが言及したビルダーパターンはDTOを作成するもう一つの方法です(私は単純化のためにコンストラクタを使用しました)。もしあなたがビルダーを使いたいなら、それはあなた次第です。あなたはPersonDtoWithFaxBuilder'インスタンスを返す 'withFax(...)'を使って独自のビルダーを簡単に作ることができます。しかし、ビルダーを導入すると、通常はいくらか複雑になります。あなたが決める。 –

関連する問題