Wiele GSON @SerializedName na pole?

105

Czy istnieje sposób w Gson, aby zmapować wiele pól JSON na jedną zmienną składową obiektu Java?

Powiedzmy, że mam klasę Java ...

public class MyClass {
    String id;
    String name;
}

Chcę używać tej jednej klasy z dwoma różnymi usługami. Jednak te dwie usługi różnią się sposobem, w jaki zwracają swoje dane ...

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

... i ...

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

... odpowiednio.

Czy istnieje sposób odwzorowania pól "person"i "user"w ciągu JSON na namepole w obiekcie Java?

(Uwaga: potrzebuję tylko konwersji z ciągu JSON na obiekt Java - nigdy na odwrót.)

Gus
źródło
1
Oto proste i doskonałe wyjaśnienie futurestud.io/tutorials/ ...
Atul Bhardwaj

Odpowiedzi:

242

W październiku 2015 r. W wersji 2.4 Gson ( dziennik zmian ) dodano możliwość używania alternatywnych / wielu nazw @SerializedNamepodczas deserializacji. Nie potrzeba więcej niestandardowego TypeAdapter!

Stosowanie:

@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
źródło
1
Oto proste i doskonałe wyjaśnienie futurestud.io/tutorials/ ...
Atul Bhardwaj
Niesamowity! Dziękuję za tę odpowiedź!
sunlover3
27

Nie jest obsługiwane definiowanie wielu @SerializedNameadnotacji w polu w Gson.

Przyczyna: domyślnie deserializacja jest zarządzana za pomocą LinkedHashMap, a klucze są definiowane przez nazwy pól przychodzących json (nie nazwy pól klasy niestandardowej lub serializedNames) i istnieje mapowanie jeden do jednego. Widać realizacji (jak działa deserializacjia) na ReflectiveTypeAdapterFactoryklasę za wewnętrzną klasy Adapter<T>„s read(JsonReader in)metody.

Rozwiązanie: Możesz napisać własny TypeAdapter który obsługuje name, personi userznaczniki json i mapuje je do nazwy pola niestandardowej klasy 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();
    }
}

Przypadek testowy:

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

Przykłady dotyczące TypeAdapters.

Edycja 2016.04.06: Jak napisał @Mathieu Castets w odpowiedzi, jest teraz obsługiwana. (To jest poprawna odpowiedź na to pytanie.)

public abstract String [] alternate
Returns: alternatywne nazwy pola podczas deserializacji
Domyślnie: {}

Devrim
źródło
Oto proste i doskonałe wyjaśnienie futurestud.io/tutorials/ ...
Atul Bhardwaj
20

dla fanów Kotlina

@SerializedName(value="name", alternate= ["person", "user"])
Mostafa Anter
źródło
8

W przypadku KOTLIN użyłem poniżej, ale nie działa

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

więc go zredagowałem i tutaj działa dobrze !!

@SerializedName(value="name", alternate= arrayOf("person", "user"))
android
źródło