it-swarm.com.de

Gson: Wie Sie bestimmte Felder ohne Anmerkungen von der Serialisierung ausschließen

Ich versuche Gson zu lernen und kämpfe 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 wie firstName oder country hinzufügen, aber ich schaffe es nicht, Eigenschaften bestimmter Felder wie country.name auszuschließen.

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

Ich würde mich über eine Hilfe bei der Lösung dieses Problems freuen.

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

Vielen Dank

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

mit Gson

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

Edit: Ich habe die Frage mit folgendem Zusatz erneut geöffnet:

Ich habe ein zweites Feld mit demselben Typ hinzugefügt, um dieses Problem weiter zu klären. Grundsätzlich möchte ich country.name ausschließen, aber nicht countrOfBirth.name. Ich möchte auch nicht Country als Typ ausschließen .. Die Typen sind also die gleichen, es ist die tatsächliche Stelle im Objektdiagramm, die ich genau festlegen und ausschließen möchte.

386
Liviu T.

Für alle Felder, die Sie nicht serialisieren möchten, sollten Sie in der Regel den Modifikator "Transient" verwenden. Dies gilt auch für Json-Serialisierer (zumindest für einige wenige, die ich verwendet habe, einschließlich Gson).

Wenn Sie nicht möchten, dass der Name im serialisierten json angezeigt wird, geben Sie ihm ein vorübergehendes Schlüsselwort, z.

private transient String name;

Weitere Details in der Gson-Dokumentation

585
Chris Seline

Nishant bot eine gute Lösung, aber es gibt einen einfacheren Weg. Markieren Sie einfach die gewünschten Felder mit der @Expose-Annotation.

@Expose private Long id;

Lassen Sie alle Felder aus, die Sie nicht serialisieren möchten. Dann erstellen Sie einfach Ihr Gson-Objekt auf diese Weise:

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

Sie möchten also ausschließenfirstName und country.name. So sollte Ihre ExclusionStrategy 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 sehen, wird true für Student.firstName und Country.name zurückgegeben, was Sie ausschließen möchten.

Sie müssen diese ExclusionStrategy so anwenden,

    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 gibt zurück:

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

Ich gehe davon aus, dass das Länderobjekt in der Studentenklasse mit id = 91L initialisiert wird.


Sie können Lust bekommen. Beispielsweise möchten Sie kein Feld serialisieren, dessen Name die Zeichenfolge "name" enthält. Mach das:

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

Dies wird zurückkehren:

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

EDIT: Je nach Wunsch weitere Informationen hinzugefügt.

Dieses ExclusionStrategy erledigt das, 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));
        }

    }

So können wir es generisch verwenden.

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

Es gibt zurück:

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

Nachdem ich alle verfügbaren Antworten gelesen hatte, stellte ich fest, dass es am flexibelsten war, die benutzerdefinierte @Exclude-Annotation zu verwenden. Also habe ich eine einfache Strategie dafür implementiert (ich wollte nicht alle Felder mit @Expose markieren oder transient verwenden, was in der app Serializable-Serialisierung zu Konflikten führte):

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

Verwendungszweck:

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

Ich bin auf dieses Problem gestoßen, in dem ich einige wenige Felder hatte, die ich nur von der Serialisierung ausschließen wollte. Daher entwickelte ich eine relativ einfache Lösung, die die @Expose-Annotation von Gson mit benutzerdefinierten Ausschlussstrategien verwendet.

Die einzige integrierte Methode für die Verwendung von @Expose ist die Einstellung von GsonBuilder.excludeFieldsWithoutExposeAnnotation(). Felder, die keinen expliziten @Expose enthalten, werden jedoch, wie der Name schon sagt, ignoriert. Da ich nur wenige Felder hatte, die ich ausschließen wollte, fand ich die Möglichkeit, die Anmerkung zu jedem Feld hinzuzufügen, sehr umständlich.

Ich wollte eigentlich die Umkehrung, in der alles enthalten war, es sei denn, ich habe ausdrücklich @Expose verwendet, um es auszuschließen. Ich habe die folgenden Ausschlussstrategien verwendet, um dies zu erreichen:

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

Nun kann ich einige Felder mit @Expose(serialize = false)- oder @Expose(deserialize = false)-Annotationen ausschließen (beachten Sie, dass der Standardwert für beide @Expose-Attribute true ist). Sie können natürlich @Expose(serialize = false, deserialize = false) verwenden, dies wird jedoch präziser ausgeführt, indem Sie stattdessen das Feld transient deklarieren (das bei diesen benutzerdefinierten Ausschlussstrategien immer noch wirksam ist).

75
Derek Shockey

Ich habe mir eine Klassenfabrik ausgedacht, um diese Funktionalität zu unterstützen. Übergeben Sie eine beliebige Kombination von Feldern oder Klassen, die Sie ausschließen möchten.

public class GsonFactory {

    public static Gson build(final List<String> fieldExclusions, final List<Class<?>> classExclusions) {
        GsonBuilder b = new GsonBuilder();
        b.addSerializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes f) {
                return fieldExclusions == null ? false : fieldExclusions.contains(f.getName());
            }

            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
                return classExclusions == null ? false : classExclusions.contains(clazz);
            }
        });
        return b.create();

    }
}

Erstellen Sie zur Verwendung zwei Listen (jede ist optional) und erstellen Sie Ihr GSON-Objekt:

static {
 List<String> fieldExclusions = new ArrayList<String>();
 fieldExclusions.add("id");
 fieldExclusions.add("provider");
 fieldExclusions.add("products");

 List<Class<?>> classExclusions = new ArrayList<Class<?>>();
 classExclusions.add(Product.class);
 GSON = GsonFactory.build(null, classExclusions);
}

private static final Gson GSON;

public String getSomeJson(){
    List<Provider> list = getEntitiesFromDatabase();
    return GSON.toJson(list);
}
16
Domenic D.

Sie können den Json-Baum mit Gson erkunden.

Versuchen Sie etwas so: 

gson.toJsonTree(student).getAsJsonObject()
.get("country").getAsJsonObject().remove("name");

Sie können auch einige Eigenschaften hinzufügen:

gson.toJsonTree(student).getAsJsonObject().addProperty("isGoodStudent", false);

Getestet mit gson 2.2.4.

15

Ich habe dieses Problem mit benutzerdefinierten Anmerkungen gelöst .. Dies ist meine Anmerkungsklasse "SkipSerialization": 

@Target (ElementType.FIELD)
public @interface SkipSerialisation {

}

und das ist mein GsonBuilder:

gsonBuilder.addSerializationExclusionStrategy(new ExclusionStrategy() {

  @Override public boolean shouldSkipField (FieldAttributes f) {

    return f.getAnnotation(SkipSerialisation.class) != null;

  }

  @Override public boolean shouldSkipClass (Class<?> clazz) {

    return false;
  }
});

Beispiel:

public class User implements Serializable {

  public String firstName;

  public String lastName;

  @SkipSerialisation
  public String email;
}
13
Reyske

Oder kann sagen, was Felder nicht ausstellen werden mit:

Gson gson = gsonBuilder.excludeFieldsWithModifiers(Modifier.TRANSIENT).create();

auf Ihre Klasse über Attribut:

private **transient** boolean nameAttribute;
8
lucasddaniel

Ich habe diese Strategie angewendet: Ich habe jedes Feld ausgeschlossen, das nicht mit @SerializedName Annotation markiert ist,

public class Dummy {

    @SerializedName("VisibleValue")
    final String visibleValue;
    final String hiddenValue;

    public Dummy(String visibleValue, String hiddenValue) {
        this.visibleValue = visibleValue;
        this.hiddenValue = hiddenValue;
    }
}


public class SerializedNameOnlyStrategy implements ExclusionStrategy {

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

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


Gson gson = new GsonBuilder()
                .setExclusionStrategies(new SerializedNameOnlyStrategy())
                .create();

Dummy dummy = new Dummy("I will see this","I will not see this");
String json = gson.toJson(dummy);

Es kehrt zurück

{"VisibleValue": "Ich werde das sehen"}

6
MadMurdok

Ein weiterer Ansatz (besonders nützlich, wenn Sie entscheiden müssen, ob ein Feld zur Laufzeit ausgeschlossen werden soll), ist die Registrierung eines TypeAdapter in Ihrer gson-Instanz. Beispiel unten:

Gson gson = new GsonBuilder()
.registerTypeAdapter(BloodPressurePost.class, new BloodPressurePostSerializer())

In dem folgenden Fall würde der Server einen von zwei Werten erwarten. Da sie jedoch beide Werte sind, werden sie beide von gson serialisiert. Mein Ziel war es, jeden Wert, der Null ist (oder weniger), aus dem auf dem Server geposteten Json auszulassen.

public class BloodPressurePostSerializer implements JsonSerializer<BloodPressurePost> {

    @Override
    public JsonElement serialize(BloodPressurePost src, Type typeOfSrc, JsonSerializationContext context) {
        final JsonObject jsonObject = new JsonObject();

        if (src.systolic > 0) {
            jsonObject.addProperty("systolic", src.systolic);
        }

        if (src.diastolic > 0) {
            jsonObject.addProperty("diastolic", src.diastolic);
        }

        jsonObject.addProperty("units", src.units);

        return jsonObject;
    }
}
6
Damian

Kotlins @Transientannotation macht offensichtlich auch den Trick.

data class Json(
    @field:SerializedName("serialized_field_1") val field1: String,
    @field:SerializedName("serialized_field_2") val field2: String,
    @Transient val field3: String
)

Ausgabe:

{"serialized_field_1":"VALUE1","serialized_field_2":"VALUE2"}
1
lookashc

Ich arbeite nur mit der @Expose-Anmerkung, hier meiner Version, die ich verwende

compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'

In der Variable Model:

@Expose
int number;

public class AdapterRestApi {

In der Adapter-Klasse:

public EndPointsApi connectRestApi() {
    OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(90000, TimeUnit.SECONDS)
            .readTimeout(90000,TimeUnit.SECONDS).build();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(ConstantRestApi.ROOT_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build();

    return retrofit.create  (EndPointsApi.class);
}
1
devitoper

Das was ich immer benutze:

Das in Gson implementierte Standardverhalten ist, dass Nullobjektfelder ignoriert werden.

Bedeutet, dass das Gson-Objekt keine Felder mit Nullwerten für JSON serialisiert. Wenn ein Feld in einem Java-Objekt null ist, wird es von Gson ausgeschlossen.

Mit dieser Funktion können Sie ein Objekt in ein von Ihnen festgelegtes Null-Objekt konvertieren

     /**
   * convert object to json
   */
  public String toJson(Object obj) {
    // Convert emtpy string and objects to null so we don't serialze them
    setEmtpyStringsAndObjectsToNull(obj);
    return gson.toJson(obj);
  }

  /**
   * Sets all empty strings and objects (all fields null) including sets to null.
   *
   * @param obj any object
   */
  public void setEmtpyStringsAndObjectsToNull(Object obj) {
    for (Field field : obj.getClass().getDeclaredFields()) {
      field.setAccessible(true);
      try {
        Object fieldObj = field.get(obj);
        if (fieldObj != null) {
          Class fieldType = field.getType();
          if (fieldType.isAssignableFrom(String.class)) {
            if(fieldObj.equals("")) {
              field.set(obj, null);
            }
          } else if (fieldType.isAssignableFrom(Set.class)) {
            for (Object item : (Set) fieldObj) {
              setEmtpyStringsAndObjectsToNull(item);
            }
            boolean setFielToNull = true;
            for (Object item : (Set) field.get(obj)) {
              if(item != null) {
                setFielToNull = false;
                break;
              }
            }
            if(setFielToNull) {
              setFieldToNull(obj, field);
            }
          } else if (!isPrimitiveOrWrapper(fieldType)) {
            setEmtpyStringsAndObjectsToNull(fieldObj);
            boolean setFielToNull = true;
            for (Field f : fieldObj.getClass().getDeclaredFields()) {
              f.setAccessible(true);
              if(f.get(fieldObj) != null) {
                setFielToNull = false;
                break;
              }
            }
            if(setFielToNull) {
              setFieldToNull(obj, field);
            }
          }
        }
      } catch (IllegalAccessException e) {
        System.err.println("Error while setting empty string or object to null: " + e.getMessage());
      }
    }
  }

  private void setFieldToNull(Object obj, Field field) throws IllegalAccessException {
    if(!Modifier.isFinal(field.getModifiers())) {
      field.set(obj, null);
    }
  }

  private boolean isPrimitiveOrWrapper(Class fieldType)  {
    return fieldType.isPrimitive()
        || fieldType.isAssignableFrom(Integer.class)
        || fieldType.isAssignableFrom(Boolean.class)
        || fieldType.isAssignableFrom(Byte.class)
        || fieldType.isAssignableFrom(Character.class)
        || fieldType.isAssignableFrom(Float.class)
        || fieldType.isAssignableFrom(Long.class)
        || fieldType.isAssignableFrom(Double.class)
        || fieldType.isAssignableFrom(Short.class);
  }
0
Julian Solarte

Ich habe eine Kotlin-Version 

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
internal annotation class JsonSkip

class SkipFieldsStrategy : ExclusionStrategy {

    override fun shouldSkipClass(clazz: Class<*>): Boolean {
        return false
    }

    override fun shouldSkipField(f: FieldAttributes): Boolean {
        return f.getAnnotation(JsonSkip::class.Java) != null
    }
}

und wie Sie dies zu Retrofit GSONConverterFactory hinzufügen können: 

val gson = GsonBuilder()
                .setExclusionStrategies(SkipFieldsStrategy())
                //.serializeNulls()
                //.setDateFormat(DateFormat.LONG)
                //.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
                //.setPrettyPrinting()
                //.registerTypeAdapter(Id.class, IdTypeAdapter())
                .create()
        return GsonConverterFactory.create(gson)
0
Michał Ziobro