Вопрос-ответ

Gson: How to exclude specific fields from Serialization without annotations

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

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

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

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

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

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

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

Переведено автоматически
Ответ 1

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

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

private transient String name;

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

Ответ 2

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

@Expose private Long id;

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

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Ответ 3

Итак, вы хотите исключить 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 for 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.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91}}

Я предполагаю, что объект country инициализирован с помощью id = 91L в классе student .


Вам может показаться необычным. Например, вы не хотите сериализовать любое поле, в имени которого содержится строка "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 }}
Ответ 4

Прочитав все доступные ответы, я обнаружил, что наиболее гибким, в моем случае, было использование пользовательских @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();
java json serialization