Gson: Как исключить определенные поля из сериализации без аннотаций

Я пытаюсь узнать Гссона, и я борюсь с исключением поля. Вот мои классы

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

Я могу использовать GsonBuilder и добавить ExclusionStrategy для имени поля, такого как firstName или country, но мне кажется, что мне не удается исключить свойства определенных полей, например country.name.

Используя метод public boolean shouldSkipField(FieldAttributes fa), FieldAttributes не содержит достаточной информации, чтобы соответствовать полю с фильтром, например country.name.

Я был бы признателен за любую помощь в решении этой проблемы.

P.S: Я хочу избежать аннотаций, так как я хочу улучшить это и использовать RegEx для фильтрации полей.

Спасибо

Изменить. Я пытаюсь понять, можно ли эмулировать поведение плагин Struts2 JSON

с помощью Gson

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

Edit:  Я снова открыл вопрос со следующим дополнением:

Я добавил второе поле с тем же типом, чтобы уточнить эту проблему. В основном я хочу исключить country.name, но не countrOfBirth.name. Я также не хочу исключать страну как тип. Таким образом, типы являются тем же самым фактическим местом в графе объектов, который я хочу точно определить и исключить.

355
задан 26 янв. '11 в 12:19
источник поделиться
12 ответов

Любые поля, которые вы не хотите сериализовать вообще, должны использовать модификатор "переходный", и это также относится к сериализаторам json (по крайней мере, это относится к нескольким, которые я использовал, включая gson).

Если вы не хотите, чтобы имя отображалось в сериализованном json, дайте ему временное ключевое слово, например:

private transient String name;

Подробнее в документации Gson

561
ответ дан 04 мая '11 в 23:40
источник

Нишант обеспечил хорошее решение, но там был более простой способ. Просто отметьте нужные поля аннотацией @Expose, например:

@Expose private Long id;

Оставьте все поля, которые вы не хотите сериализовать. Затем просто создайте объект Gson таким образом:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
288
ответ дан 18 окт. '11 в 20:26
источник

Таким образом, вы хотите исключить firstName и country.name. Вот как выглядит ваша ExclusionStrategy

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

    }

Если вы видите, точно она возвращает true для Student.firstName и Country.name, который является то, что вы хотите исключить.

Вам нужно применить эту ExclusionStrategy как это,

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

Это возвращает:

{ "MiddleName": "J", "инициалы": "ПФ", "LastName": "Фрай", "страна": { "ID": 91}}

Я предполагаю, что объект страны инициализируется id = 91L в классе ученика.


Вы можете получить фантазию. Например, вы не хотите сериализовать любое поле, содержащее "имя" в его имени. Сделай это:

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

Это вернет:

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

EDIT: добавлено больше информации по запросу.

Эта ExclusionStrategy будет делать это, но вам нужно передать "Полностью квалифицированное имя поля". Смотри ниже:

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

    }

Вот как мы можем использовать его в целом.

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

Он возвращает:

{ "firstName": "Philip" , "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91 }}
208
ответ дан 26 янв. '11 в 13:13
источник

Прочитав все доступные ответы, я узнал, что наиболее гибким, в моем случае, было использование пользовательской аннотации @Exclude. Таким образом, я реализовал простую стратегию для этого (я не хотел отмечать все поля с помощью @Expose, и я не хотел использовать transient, который противоречил сериализации приложения Serializable):

Аннотация:

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

Стратегия:

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

Использование:

new GsonBuilder().setExclusionStrategies(new AnnotationExclusionStrategy()).create();
175
ответ дан 16 янв. '15 в 18:04
источник

Я столкнулся с этой проблемой, в которой у меня было небольшое количество полей, которые я хотел исключить только из сериализации, поэтому я разработал довольно простое решение, которое использует аннотацию Gson @Expose с пользовательскими стратегиями исключения.

Единственным встроенным способом использования @Expose является установка GsonBuilder.excludeFieldsWithoutExposeAnnotation(), но по мере того, как имя указывает, поля без явного @Expose игнорируются. Поскольку у меня было только несколько полей, которые я хотел исключить, я нашел перспективу добавления аннотации к каждой области очень громоздкой.

Мне действительно нужен обратный, в котором все было включено, если я явно не использовал @Expose, чтобы исключить его. Для этого я использовал следующие стратегии исключения:

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

Теперь я могу легко исключить несколько полей с аннотациями @Expose(serialize = false) или @Expose(deserialize = false) (обратите внимание, что значением по умолчанию для обоих атрибутов @Expose является true). Разумеется, вы можете использовать @Expose(serialize = false, deserialize = false), но это более точно достигается путем объявления поля transient вместо этого (которое все еще вступает в силу с этими пользовательскими стратегиями исключения).

73
ответ дан 18 июля '13 в 23:46
источник

Я разработал класс factory для поддержки этой функции. Передайте любую комбинацию полей или классов, которые вы хотите исключить.

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

    }
}

Чтобы использовать, создайте два списка (каждый необязательно) и создайте свой объект GSON:

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
ответ дан 26 апр. '12 в 21:03
источник

Вы можете исследовать дерево json с помощью gson.

Попробуйте что-то вроде этого:

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

Вы также можете добавить некоторые свойства:

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

Протестировано с помощью gson 2.2.4.

15
ответ дан 06 февр. '14 в 11:16
источник

Я решил эту проблему с помощью специальных аннотаций. Это мой класс аннотации SkipSerialisation:

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

}

и это мой 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;
  }
});

Пример:

public class User implements Serializable {

  public String firstName;

  public String lastName;

  @SkipSerialisation
  public String email;
}
12
ответ дан 12 сент. '16 в 12:35
источник

Или можно сказать, какие поля не будут отображаться с помощью:

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

в вашем классе по атрибуту:

private **transient** boolean nameAttribute;
8
ответ дан 02 июня '13 в 23:42
источник

Другой подход (особенно полезный, если вам нужно принять решение об исключении поля во время выполнения) - зарегистрировать TypeAdapter с вашим экземпляром gson. Пример ниже:

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

В приведенном ниже случае сервер будет ожидать одно из двух значений, но поскольку оба они были int, тогда gson будет сериализовывать их оба. Моя цель состояла в том, чтобы опустить любое значение, которое равно нулю (или меньше) из json, отправленного на сервер.

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
ответ дан 27 авг. '15 в 16:53
источник

Я использовал эту стратегию: Я исключил каждое поле, которое не, помечено аннотацией @SerializedName, то есть:

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

Он возвращает

{ "VisibleValue": "Я увижу это" }

5
ответ дан 20 июля '16 в 15:15
источник

Я работаю, просто помещая аннотацию @Expose, здесь моя версия, которую я использую

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

В классе Model:

@Expose
int number;

public class AdapterRestApi {

В классе Adapter:

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);
}
0
ответ дан 31 марта '17 в 19:26
источник

Другие вопросы по меткам или Задайте вопрос