Gson: Как исключить определенные поля из сериализации без аннотаций - PullRequest
396 голосов
/ 26 января 2011

Я пытаюсь выучить 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.

. Буду признателен за любую помощь с решением дляэта проблема.

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

Спасибо

Редактировать : я пытаюсь понять, возможно ли имитировать поведение JSON-плагина Struts2

с использованием Gson

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

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

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

Ответы [ 15 ]

601 голосов
/ 05 мая 2011

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

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

private transient String name;

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

305 голосов
/ 18 октября 2011

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

@Expose private Long id;

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

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
226 голосов
/ 26 января 2011

Итак, вы хотите исключить 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": "Дж.", "Инициалы": "P.F", "LastName": "Фрай", "страна": { "ID": 91}}

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


Вы можете стать фантазером. Например, вы не хотите сериализовать любое поле, которое содержит в своем имени строку «name». Сделайте это:

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

Это вернет:

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

РЕДАКТИРОВАТЬ: Добавлено больше информации по запросу.

Это 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 }}
201 голосов
/ 16 января 2015

После прочтения всех доступных ответов я обнаружил, что наиболее гибким в моем случае было использование пользовательской аннотации @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();
76 голосов
/ 19 июля 2013

Я столкнулся с этой проблемой, в которой у меня было небольшое количество полей, которые я хотел исключить только из сериализации, поэтому я разработал довольно простое решение, в котором используется аннотация 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 (которое все еще действует при использовании этих пользовательских стратегий исключения).

17 голосов
/ 06 февраля 2014

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

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

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

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

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

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

16 голосов
/ 26 апреля 2012

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

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);
}
13 голосов
/ 12 сентября 2016

Я решил эту проблему с помощью пользовательских аннотаций. Это мой класс аннотации "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;
}
8 голосов
/ 03 июня 2013

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

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

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

private **transient** boolean nameAttribute;
7 голосов
/ 20 июля 2016

Я использовал эту стратегию: я исключил все поля, которые не отмечены @ 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": "Я увижу это"}

...