457 Stimmen

Gson: Wie kann man bestimmte Felder von der Serialisierung ohne Annotationen ausschließen?

Ich versuche, Gson zu lernen, und habe Probleme mit dem Feldausschluss. Hier sind meine Klassen

public class Student {    
  private Long                id;
  private String              firstName        = "Philip";
  private String              middleName       = "J.";
  private String              initials         = "P.F";
  private String              lastName         = "Fry";
  private Country             country;
  private Country             countryOfBirth;
}

public class Country {    
  private Long                id;
  private String              name;
  private Object              other;
}

Ich kann den GsonBuilder verwenden und eine ExclusionStrategy für einen Feldnamen hinzufügen wie firstName o country aber ich scheine es nicht zu schaffen, Eigenschaften bestimmter Felder auszuschließen, wie country.name .

Anwendung der Methode public boolean shouldSkipField(FieldAttributes fa) enthält FieldAttributes nicht genügend Informationen, um das Feld mit einem Filter wie country.name .

P.S.: Ich möchte Anmerkungen vermeiden, da ich dies verbessern und RegEx verwenden möchte, um Felder herauszufiltern.

bearbeiten : Ich versuche zu sehen, ob es möglich ist, das Verhalten von Struts2 JSON-Plugin

mit Gson

<interceptor-ref name="json">
  <param name="enableSMD">true</param>
  <param name="excludeProperties">
    login.password,
    studentList.*\.sin
  </param>
</interceptor-ref>

Bearbeiten: Ich habe die Frage mit folgendem Zusatz erneut gestellt:

Ich habe ein zweites Feld desselben Typs hinzugefügt, um das Problem weiter zu klären. Im Grunde möchte ich Folgendes ausschließen country.name aber nicht countrOfBirth.name . Ich möchte auch Land als Typ nicht ausschließen. Die Typen sind also dieselben, es ist die tatsächliche Stelle im Objektdiagramm, die ich genau bestimmen und ausschließen möchte.

671voto

Chris Seline Punkte 6812

Alle Felder, die Sie nicht wollen, serialisiert im Allgemeinen sollten Sie die "transient" Modifikator, und dies gilt auch für json serializers (zumindest tut es auf ein paar, die ich verwendet haben, einschließlich gson).

Wenn Sie nicht möchten, dass name in der serialisierten json-Datei erscheint, geben Sie ein transientes Schlüsselwort an, z.B:

private transient String name;

Weitere Einzelheiten in der Gson-Dokumentation

327voto

JayPea Punkte 9183

Nishant hat eine gute Lösung gefunden, aber es gibt einen einfacheren Weg. Markieren Sie einfach die gewünschten Felder mit der @Expose-Anmerkung, wie z. B.:

@Expose private Long id;

Lassen Sie alle Felder weg, die Sie nicht serialisieren wollen. Dann erstellen Sie Ihr Gson-Objekt einfach auf diese Weise:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

251voto

Nishant Punkte 51997

Sie wollen also ausschließen firstName y country.name . Hier ist, was Ihr ExclusionStrategy sollte wie folgt aussehen

    public class TestExclStrat implements ExclusionStrategy {

        public boolean shouldSkipClass(Class<?> arg0) {
            return false;
        }

        public boolean shouldSkipField(FieldAttributes f) {

            return (f.getDeclaringClass() == Student.class && f.getName().equals("firstName"))||
            (f.getDeclaringClass() == Country.class && f.getName().equals("name"));
        }

    }

Wenn Sie genau hinsehen, gibt es true para Student.firstName y Country.name was Sie ausschließen wollen.

Sie müssen dies anwenden ExclusionStrategy wie diese,

    Gson gson = new GsonBuilder()
        .setExclusionStrategies(new TestExclStrat())
        //.serializeNulls() <-- uncomment to serialize NULL fields as well
        .create();
    Student src = new Student();
    String json = gson.toJson(src);
    System.out.println(json);

Dies führt zurück:

{ "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91}}

Ich gehe davon aus, dass das Land-Objekt initialisiert wird mit id = 91L in der Schülerklasse.


Sie können sich etwas einfallen lassen. Sie möchten zum Beispiel kein Feld serialisieren, das die Zeichenfolge "name" im Namen enthält. Tun Sie dies:

public boolean shouldSkipField(FieldAttributes f) {
    return f.getName().toLowerCase().contains("name"); 
}

Dies wird zurückgegeben:

{ "initials": "P.F", "country": { "id": 91 }}

EDIT: Weitere Informationen wie gewünscht hinzugefügt.

Diese ExclusionStrategy erledigt die Aufgabe, aber Sie müssen "Fully Qualified Field Name" übergeben. Siehe unten:

    public class TestExclStrat implements ExclusionStrategy {

        private Class<?> c;
        private String fieldName;
        public TestExclStrat(String fqfn) throws SecurityException, NoSuchFieldException, ClassNotFoundException
        {
            this.c = Class.forName(fqfn.substring(0, fqfn.lastIndexOf(".")));
            this.fieldName = fqfn.substring(fqfn.lastIndexOf(".")+1);
        }
        public boolean shouldSkipClass(Class<?> arg0) {
            return false;
        }

        public boolean shouldSkipField(FieldAttributes f) {

            return (f.getDeclaringClass() == c && f.getName().equals(fieldName));
        }

    }

Im Folgenden wird erläutert, wie wir sie allgemein verwenden können.

    Gson gson = new GsonBuilder()
        .setExclusionStrategies(new TestExclStrat("in.naishe.test.Country.name"))
        //.serializeNulls()
        .create();
    Student src = new Student();
    String json = gson.toJson(src);
    System.out.println(json); 

Sie kehrt zurück:

{ "firstName": "Philip" , "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91 }}

246voto

pkk Punkte 3651

Nachdem ich alle verfügbaren Antworten gelesen hatte, fand ich heraus, dass es in meinem Fall am flexibelsten war, benutzerdefinierte @Exclude Bemerkung. Daher habe ich eine einfache Strategie für diese Aufgabe entwickelt (ich wollte nicht alle Felder mit @Expose noch wollte ich die transient die im Widerspruch zu den in der App Serializable Serialisierung) :

Anmerkung:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Exclude {
}

Strategie:

public class AnnotationExclusionStrategy implements ExclusionStrategy {

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        return f.getAnnotation(Exclude.class) != null;
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return false;
    }
}

Uso:

new GsonBuilder().setExclusionStrategies(new AnnotationExclusionStrategy()).create();

87voto

Derek Shockey Punkte 1777

Ich bin auf dieses Problem gestoßen, als ich eine kleine Anzahl von Feldern hatte, die ich nur von der Serialisierung ausschließen wollte, also habe ich eine ziemlich einfache Lösung entwickelt, die Gsons @Expose Annotation mit benutzerdefinierten Ausschlussstrategien.

Die einzige eingebaute Möglichkeit zur Verwendung von @Expose ist durch die Einstellung GsonBuilder.excludeFieldsWithoutExposeAnnotation() aber wie der Name schon sagt, können Felder ohne explizite @Expose werden ignoriert. Da ich nur einige wenige Felder ausschließen wollte, fand ich die Aussicht, die Anmerkung zu jedem Feld hinzuzufügen, sehr mühsam.

Ich wollte eigentlich den umgekehrten Fall, in dem alles enthalten war, es sei denn, ich verwendete ausdrücklich @Expose um sie auszuschließen. Dazu habe ich die folgenden Ausschlussstrategien verwendet:

new GsonBuilder()
        .addSerializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes fieldAttributes) {
                final Expose expose = fieldAttributes.getAnnotation(Expose.class);
                return expose != null && !expose.serialize();
            }

            @Override
            public boolean shouldSkipClass(Class<?> aClass) {
                return false;
            }
        })
        .addDeserializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes fieldAttributes) {
                final Expose expose = fieldAttributes.getAnnotation(Expose.class);
                return expose != null && !expose.deserialize();
            }

            @Override
            public boolean shouldSkipClass(Class<?> aClass) {
                return false;
            }
        })
        .create();

Jetzt kann ich ganz einfach einige Felder ausschließen mit @Expose(serialize = false) o @Expose(deserialize = false) Anmerkungen (beachten Sie, dass der Standardwert für beide @Expose Attribute ist true ). Sie können natürlich auch @Expose(serialize = false, deserialize = false) aber das lässt sich besser erreichen, indem man das Feld transient (die auch bei diesen benutzerdefinierten Ausschlussstrategien wirksam ist).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X