serialize - java gson google




Gson: Como excluir campos específicos da serialização sem anotações (10)

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         = "P.F";
  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 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 este problema.

PS: Eu quero evitar anotações, 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

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

Edit: reabri a questão 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 objetos que quero identificar e excluir.


A anotação @Transient de Kotlin também faz o truque aparentemente.

data class Json(
    @field:SerializedName("serialized_field_1") val field1: String,
    @field:SerializedName("serialized_field_2") val field2: String,
    @Transient val field3: String
)

Saída:

{"serialized_field_1":"VALUE1","serialized_field_2":"VALUE2"}

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 transient que conflitassem 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();

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 classe de Model :

@Expose
int number;

public class AdapterRestApi {

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

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

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

    }
}

Para usar, crie duas listas (cada uma é opcional) e crie seu objeto 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);
}

Eu resolvi esse problema com anotações personalizadas. Esta é a minha classe 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;
}

Eu tenho a versão Kotlin

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
internal annotation class JsonSkip

class SkipFieldsStrategy : ExclusionStrategy {

    override fun shouldSkipClass(clazz: Class<*>): Boolean {
        return false
    }

    override fun shouldSkipField(f: FieldAttributes): Boolean {
        return f.getAnnotation(JsonSkip::class.java) != null
    }
}

e como você pode adicionar isso ao Retrofit GSONConverterFactory:

val gson = GsonBuilder()
                .setExclusionStrategies(SkipFieldsStrategy())
                //.serializeNulls()
                //.setDateFormat(DateFormat.LONG)
                //.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
                //.setPrettyPrinting()
                //.registerTypeAdapter(Id.class, IdTypeAdapter())
                .create()
        return GsonConverterFactory.create(gson)

Nishant forneceu uma boa solução, mas há uma maneira mais fácil. Simplesmente marque 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 objeto Gson desta maneira:

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

Ou pode dizer quais campos não serão expostos com:

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

na sua classe no atributo:

private **transient** boolean nameAttribute;

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


Você pode explorar a árvore json com gson.

Tente algo como isto:

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.







gson