2017-03-01 3 views
4

ein POJO Gegeben:Gson Serialisierung: ausschließen einige Felder, wenn null, aber nicht alle von ihnen

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

I „phone“ wollen jederzeit serialisiert werden, aber „Fax“ nur dann, wenn es nicht null ist. Gegeben, so eine Person "John" ohne Telefon oder Fax:

Aktuell:

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

Was ich brauche:

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

Gibt es so etwas wie:

@Expose (serialize_if_null = false) 

transienten funktioniert nicht, weil ich es immer noch serialisiert haben möchte, wenn es einen Wert hat.

Mit ExclusionStrategy kann ich das Feld definieren, aber ich kann keinen Weg finden, den Wert zu erhalten.

Dank

Antwort

1

Mit ExclusionStrategy kann ich das Feld definieren, aber ich kann nicht einen Weg zu finden, scheinen den Wert zu erhalten.

Ja, es bietet keine Möglichkeit, den aktuellen Feldwert zu bestimmen. Dies liegt daran, wie Gson ReflectiveTypeAdapterFactory arbeitet intern (die BoundField.serialized ist final und nur einmal aufgelöst):

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

Dieses Verhalten kann nicht geändert werden, aber ich glaube, es ist eine gute Design-Wahl Anwendungsobjekte zu trennen und ihre Serialisierte Darstellungen (siehe Muster Data Transfer Object), um Konzepte nicht zu vermischen und Anwendungskomponenten lose gekoppelt zu machen (eine Migration von Gson würde eines Tages nur Modifikationen für die jeweiligen DTO-Klassen erfordern).

Wenn Sie mit mit DTOs eingeführt, um die Anwendung in Ordnung sind, dann könnten Sie separate DTO Klassen für beide Szenarien erstellen: Erhaltung phone und fax auf dem fax Feldwert abhängig zu verwerfen.

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

In diesem Fall ist die Serialisierung straight-forward:

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

Wenn Sie die getrennten DTO Konzept per se nicht einführen wollen, haben Sie wahrscheinlich eine benutzerdefinierte implementieren möchten Serialisierer, der in der Implementierung etwas komplizierter ist und wahrscheinlich etwas fehlerbehaftet ist, weil Eigenschaftsnamen hardcoding sind (aber Sie können natürlich gute Tests haben oder die Namen aus java.lang.reflect.Field Instanzen extrahieren).

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

} 

Ich bin nicht wirklich sicher, aber ich denke, dass eher für die Serialisierung Zwecke JSON Bäume zu schaffen als DTOs mit von dem Speicherverbrauch Sicht teuer etwas mehr sein kann (zumindest wegen komplizierter JsonElement Struktur) .

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

Beide Lösungen Ausgabe:

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

+0

upvoted. das "Person" -Objekt ist nur ein vereinfachtes Beispiel, also ist die DTO-Stil-Lösung möglicherweise nicht die sauberste Art zu gehen ... wahrscheinlich etwas wie personbuilder(). withfax(). build() etwas? – sikidhart

+0

@sikidhart Nun, es hängt davon ab, und ich würde mit DTOs gehen, um es weniger fehleranfällig zu machen. Beachte den Trick: Gson kann zwischen 'PersonDto' und 'PersonDtoWithFax' beim tatsächlichen Objekttyp unterscheiden, wenn die 'toJson'-Methode aufgerufen wird. Das von Ihnen erwähnte Builder-Muster ist nur eine weitere Möglichkeit, ein DTO zu erstellen (ich habe Konstruktoren nur für die Einfachheit der Antwort verwendet). Wenn Sie Builder verwenden möchten, liegt alles an Ihnen, und Sie können ganz einfach Ihren eigenen Builder mit 'withFax (...)' erstellen, der eine Instanz von PersonDtoWithFaxBuilder zurückgibt. Aber die Einführung von Erbauern bringt normalerweise einige Komplexität mit sich. Du entscheidest. –

Verwandte Themen