Gson: Como excluir campos específicos da serialização sem annotations

Estou tentando aprender Gson e estou lutando com a exclusão de campo. Aqui estão minhas aulas

public class Student { private Long id; private String firstName = "Philip"; private String middleName = "J."; private String initials = "PF"; private String lastName = "Fry"; private Country country; private Country countryOfBirth; } public class Country { private Long id; private String name; private Object other; } 

Posso usar o GsonBuilder e adicionar uma ExclusionStrategy para um nome de campo como firstName ou country mas parece que não consigo excluir propriedades de determinados campos como country.name .

Usando o método public boolean shouldSkipField(FieldAttributes fa) , FieldAttributes não contém informações suficientes para corresponder ao campo com um filtro como country.name .

Eu apreciaria qualquer ajuda com uma solução para esse problema.

PS: Eu quero evitar annotations, pois quero melhorar isso e usar o RegEx para filtrar os campos.

Obrigado

Edit : Estou tentando ver se é possível emular o comportamento do plugin Struts2 JSON

usando Gson

  true  login.password, studentList.*\.sin   

Edit: reabri a pergunta com a seguinte adição:

Eu adicionei um segundo campo com o mesmo tipo para esclarecer ainda mais esse problema. Basicamente eu quero excluir country.name mas não countrOfBirth.name . Eu também não quero excluir o país como um tipo. Então, os tipos são os mesmos, é o lugar real no gráfico de objects que quero identificar e excluir.

    Quaisquer campos que você não queira serializados em geral, você deve usar o modificador “transitório”, e isso também se aplica aos serializadores json (pelo menos para alguns que eu usei, incluindo o gson).

    Se você não quer que o nome apareça no json serializado, dê a ele uma palavra-chave temporária, por exemplo:

     private transient String name; 

    Mais detalhes na documentação do Gson

    Nishant forneceu uma boa solução, mas há uma maneira mais fácil. Basta marcar os campos desejados com a anotação @Expose, como:

     @Expose private Long id; 

    Deixe de fora todos os campos que você não deseja serializar. Então apenas crie seu object Gson desta maneira:

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

    Então, você deseja excluir firstName e country.name . Aqui está como sua ExclusionStrategy deve se parecer

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

    Se você vir de perto, ele retorna true para Student.firstName e Country.name , que é o que você deseja excluir.

    Você precisa aplicar este ExclusionStrategy como este,

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

    Isso retorna:

    {"middleName": "J.", "iniciais": "PF", "lastName": "Fry", "país": {"id": 91}}

    Eu suponho que o object do país é inicializado com id = 91L na class do aluno.


    Você pode ficar chique. Por exemplo, você não deseja serializar qualquer campo que contenha a string "name" em seu nome. Faça isso:

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

    Isso retornará:

     { "initials": "PF", "country": { "id": 91 }} 

    EDIT: adicionado mais informações conforme solicitado.

    Este ExclusionStrategy vai fazer a coisa, mas você precisa passar "Nome do campo totalmente qualificado". Ver abaixo:

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

    Aqui está como podemos usá-lo genericamente.

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

    Ele retorna:

     { "firstName": "Philip" , "middleName": "J.", "initials": "PF", "lastName": "Fry", "country": { "id": 91 }} 

    Depois de ler todas as respostas disponíveis, descobri que o mais flexível, no meu caso, era usar a anotação @Exclude personalizada. Então, eu implementei uma estratégia simples para isso (eu não queria marcar todos os campos usando o @Expose nem queria usar o transient que @Expose conflito com a serialização Serializable aplicativo):

    Anotação:

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

    Estratégia:

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

    Uso:

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

    Eu me deparei com esse problema, no qual eu tinha um pequeno número de campos que eu queria excluir apenas da serialização, então desenvolvi uma solução bastante simples que usa a anotação @Expose do Gson com estratégias de exclusão personalizadas.

    A única maneira @Expose usar @Expose é definindo GsonBuilder.excludeFieldsWithoutExposeAnnotation() , mas, como o nome indica, os campos sem um @Expose explícito são ignorados. Como eu só tinha alguns campos que queria excluir, achei a possibilidade de adicionar a anotação a cada campo muito complicada.

    Eu efetivamente queria o inverso, no qual tudo estava incluído, a menos que eu explicitamente usasse @Expose para excluí-lo. Eu usei as seguintes estratégias de exclusão para conseguir isso:

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

    Agora posso excluir facilmente alguns campos com @Expose(serialize = false) ou @Expose(deserialize = false) (observe que o valor padrão para os dois atributos @Expose é true ). É claro que você pode usar @Expose(serialize = false, deserialize = false) , mas isso é feito de forma mais concisa ao declarar o campo transient (que ainda tem efeito com essas estratégias de exclusão personalizadas).

    Eu criei uma fábrica de classs para suportar essa funcionalidade. Passe em qualquer combinação de campos ou classs que você deseja excluir.

     public class GsonFactory { public static Gson build(final List fieldExclusions, final List> 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(); } } 

    Para usar, crie duas listas (cada uma é opcional) e crie seu object GSON:

     static { List fieldExclusions = new ArrayList(); fieldExclusions.add("id"); fieldExclusions.add("provider"); fieldExclusions.add("products"); List> classExclusions = new ArrayList>(); classExclusions.add(Product.class); GSON = GsonFactory.build(null, classExclusions); } private static final Gson GSON; public String getSomeJson(){ List list = getEntitiesFromDatabase(); return GSON.toJson(list); } 

    Você pode explorar a tree json com gson.

    Tente algo assim:

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

    Você pode adicionar algumas propriedades também:

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

    Testado com o gson 2.2.4.

    Eu resolvi esse problema com annotations personalizadas. Esta é a minha class de anotação “SkipSerialisation”:

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

    e este é o meu 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; } }); 

    Exemplo:

     public class User implements Serializable { public String firstName; public String lastName; @SkipSerialisation public String email; } 

    Ou pode dizer o que campos não irão expor com:

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

    na sua class no atributo:

     private **transient** boolean nameAttribute; 

    Outra abordagem (especialmente útil se você precisar tomar uma decisão para excluir um campo em tempo de execução) é registrar um TypeAdapter com sua instância do gson. Exemplo abaixo:

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

    No caso abaixo, o servidor esperaria um dos dois valores, mas como ambos eram ints, o gson serializaria ambos. Meu objective era omitir qualquer valor que fosse zero (ou menos) do json que é postado no servidor.

     public class BloodPressurePostSerializer implements JsonSerializer { @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; } } 

    Usei essa estratégia: excluí todos os campos que não estão marcados com a anotação @SerializedName , por exemplo:

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

    Devolve

    {“VisibleValue”: “Eu verei isso”}

    Estou trabalhando apenas colocando a anotação @Expose , aqui minha versão que eu uso

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

    Na class Model :

     @Expose int number; public class AdapterRestApi { 

    Na class do 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); }