Vários GSON @SerializedName por campo?

104

Existe alguma maneira no Gson de mapear vários campos JSON para uma única variável de membro de objeto Java?

Digamos que eu tenha uma classe Java ...

public class MyClass {
    String id;
    String name;
}

Desejo usar esta única classe com dois serviços diferentes. No entanto, esses dois serviços diferem na forma como eles retornam seus dados ...

{ "id": 2341, "person": "Bob" }

... e ...

{ "id": 5382, "user": "Mary" }

... respectivamente.

Existe alguma maneira de mapear os campos "person"e "user"na string JSON para o namecampo no objeto Java?

(Observação: eu só preciso converter de string JSON para objeto Java - nunca o contrário.)

Gus
fonte
1
Aqui está uma explicação simples e perfeita futurestud.io/tutorials/…
Atul Bhardwaj

Respostas:

238

Em outubro de 2015, Gson versão 2.4 ( changelog ) adicionou a capacidade de usar nomes alternativos / múltiplos para @SerializedNameao desserializar. Não é mais necessário um TypeAdapter personalizado!

Uso:

@SerializedName(value="name", alternate={"person", "user"})

https://www.javadoc.io/doc/com.google.code.gson/gson/2.6.2/com/google/gson/annotations/SerializedName.html

Mathieu Castets
fonte
1
Aqui está uma explicação simples e perfeita futurestud.io/tutorials/…
Atul Bhardwaj
Surpreendente! Obrigado por esta resposta!
sunlover3
ótima resposta, obrigado!
Dor Rud
27

Não há suporte para definir várias @SerializedNameanotações para um campo no Gson.

Motivo: por padrão, a desserialização é gerenciada com um LinkedHashMap e as chaves são definidas pelos nomes de campo json de entrada (não os nomes de campo da classe personalizada ou serializedNames) e há um mapeamento um para um. Você pode ver a implementação (como funciona a desserialização) na ReflectiveTypeAdapterFactoryclasse interna da classe Adapter<T>'s read(JsonReader in)método.

Solução: Você pode escrever um personalizado TypeAdapter que alças name, persone usertags de JSON e mapas-los para o campo nome da sua classe personalizada MyClass:

class MyClassTypeAdapter extends TypeAdapter<MyClass> {

    @Override
    public MyClass read(final JsonReader in) throws IOException {
        final MyClass myClassInstance = new MyClass();

        in.beginObject();
        while (in.hasNext()) {
            String jsonTag = in.nextName();
            if ("id".equals(jsonTag)) {
                myClassInstance.id = in.nextInt();
            } else if ("name".equals(jsonTag) 
                    || "person".equals(jsonTag)
                    || "user".equals(jsonTag)) {
                myClassInstance.name = in.nextString();
            }
        }
        in.endObject();

        return myClassInstance;
    }

    @Override
    public void write(final JsonWriter out, final MyClass myClassInstance)
            throws IOException {
        out.beginObject();
        out.name("id").value(myClassInstance.id);
        out.name("name").value(myClassInstance.name);
        out.endObject();
    }
}

Caso de teste:

    String jsonVal0 = "{\"id\": 5382, \"user\": \"Mary\" }";
    String jsonVal1 = "{\"id\": 2341, \"person\": \"Bob\"}";

    final GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(MyClass.class, new MyClassTypeAdapter());
    final Gson gson = gsonBuilder.create();

    MyClass myClassInstance0 = gson.fromJson(jsonVal0, MyClass.class);
    MyClass myClassInstance1 = gson.fromJson(jsonVal1, MyClass.class);

    System.out.println("jsonVal0 :" + gson.toJson(myClassInstance0));
    // output: jsonVal0 :{"id":5382,"name":"Mary"}

    System.out.println("jsonVal1 :" + gson.toJson(myClassInstance1));
    // output: jsonVal1 :{"id":2341,"name":"Bob"}

Exemplos sobre TypeAdapters.

Editar 2016.04.06: Como @Mathieu Castets escreveu em sua resposta, agora é compatível. (Essa é a resposta correta para esta pergunta.)

public abstract String [] alternate
Returns: os nomes alternativos do campo quando ele é desserializado
Padrão: {}

Devrim
fonte
Aqui está uma explicação simples e perfeita futurestud.io/tutorials/…
Atul Bhardwaj
20

para fãs de Kotlin

@SerializedName(value="name", alternate= ["person", "user"])
Mostafa Anter
fonte
8

Para KOTLIN eu usei abaixo, mas não funciona

@SerializedName(value="name", alternate= ["person", "user"])

então eu editei e aqui funciona bem !!

@SerializedName(value="name", alternate= arrayOf("person", "user"))
andróide
fonte