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;
Ответ 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();