Szukaj…


Wprowadzenie

Gson to biblioteka Java, której można użyć do konwersji obiektów Java na ich reprezentację JSON. Gson uważa oba te cele za bardzo ważne cele projektowe.

Funkcje Gson:

Zapewnij proste toJson() i fromJson() do konwersji obiektów Java na JSON i odwrotnie

Zezwalaj na konwersję wcześniej istniejących niemodyfikowalnych obiektów do i z JSON

Rozbudowane wsparcie dla Java Generics

Obsługa dowolnie złożonych obiektów (z głębokimi hierarchiami dziedziczenia i szerokim użyciem typów ogólnych)

Składnia

  • Excluder excluder ()
  • FieldNamingStrategy fieldNamingStrategy ()
  • <T> T fromJson (JsonElement json, klasa <T> classOfT)
  • <T> T fromJson (JsonElement json, Type typeOfT)
  • <T> T fromJson (czytnik JsonReader, typ typeOfT)
  • <T> T fromJson (Reader json, Class <T> classOfT)
  • <T> T fromJson (Reader json, Type typeOfT)
  • <T> T fromJson (String json, Class <T> classOfT)
  • <T> T fromJson (String json, Type typeOfT)
  • <T> TypeAdapter <T> getAdapter (typ klasy <T>)
  • <T> TypeAdapter <T> getAdapter (TypeToken <T> typ)
  • <T> TypeAdapter <T> getDelegateAdapter (TypeAdapterFactory skipPast, TypeToken <T> typ)
  • JsonReader newJsonReader (czytnik czytników)
  • JsonWriter newJsonWriter (pisarz)
  • JsonElement toJsonTree (Object src)
  • JsonElement toJsonTree (Object src, Type typeOfSrc)
  • boolean serializeNulls ()
  • boolean htmlSafe ()
  • String toJson (JsonElement jsonElement)
  • String toJson (Object src)
  • String toJson (Object src, Type typeOfSrc)
  • String toString ()
  • void toJson (Object src, Type typeOfSrc, Appendable writer)
  • void toJson (Object src, Type typeOfSrc, JsonWriter writer)
  • void toJson (JsonElement jsonElement, Appendable writer)
  • void toJson (JsonElement jsonElement, pisarz JsonWriter)
  • void toJson (Object src, Appendable writer)

Przetwarzanie JSON z Gsonem

Przykład pokazuje parsowanie obiektu JSON przy użyciu biblioteki Gson od Google .

Parsowanie obiektów:

class Robot {
    //OPTIONAL - this annotation allows for the key to be different from the field name, and can be omitted if key and field name are same . Also this is good coding practice as it decouple your variable names with server keys name 
    @SerializedName("version") 
    private String version;

    @SerializedName("age")
    private int age;
    
    @SerializedName("robotName")
    private String name;
    
    // optional : Benefit it allows to set default values and retain them, even if key is missing from Json response. Not required for primitive data types. 

    public Robot{
       version = "";
       name = "";
    }


}

Następnie tam, gdzie musi wystąpić parsowanie, użyj następujących poleceń:

String robotJson = "{
                        \"version\": \"JellyBean\",
                        \"age\": 3,
                        \"robotName\": \"Droid\"
                   }";

Gson gson = new Gson();
Robot robot = gson.fromJson(robotJson, Robot.class);

Analiza listy:

Podczas pobierania listy obiektów JSON często trzeba je przeanalizować i przekonwertować na obiekty Java.

Ciąg JSON, który spróbujemy przekonwertować, jest następujący:

{
  "owned_dogs": [
    {
      "name": "Ron",
      "age": 12,
      "breed": "terrier"
    },
    {
      "name": "Bob",
      "age": 4,
      "breed": "bulldog"
    },
    {
      "name": "Johny",
      "age": 3,
      "breed": "golden retriever"
    }
  ]
}

Ta konkretna tablica JSON zawiera trzy obiekty. W naszym kodzie Java będziemy chcieli zamapować te obiekty na obiekty Dog . Obiekt psa wyglądałby tak:

private class Dog {
    public String name;
    public int age;

    @SerializedName("breed")
    public String breedName;
}

Aby przekonwertować tablicę JSON na Dog[] :

Dog[] arrayOfDogs = gson.fromJson(jsonArrayString, Dog[].class);

Konwertowanie Dog[] na ciąg JSON:

String jsonArray = gson.toJson(arrayOfDogs, Dog[].class);

Aby przekonwertować tablicę JSON na ArrayList<Dog> , możemy wykonać następujące czynności:

Type typeListOfDogs = new TypeToken<List<Dog>>(){}.getType();
List<Dog> listOfDogs = gson.fromJson(jsonArrayString, typeListOfDogs);

Obiekt Type typeListOfDogs określa, jak powinna wyglądać lista obiektów Dog . GSON może użyć tego typu obiektu do odwzorowania tablicy JSON na właściwe wartości.

Alternatywnie, konwersję List<Dog> do tablicy JSON można wykonać w podobny sposób.

String jsonArray = gson.toJson(listOfDogs, typeListOfDogs);

Analiza właściwości JSON w celu wyliczenia z Gsonem

Jeśli chcesz przeanalizować ciąg znaków do wyliczenia za pomocą Gson:

{„status”: „open”}

public enum Status {
    @SerializedName("open")
    OPEN,
    @SerializedName("waiting")
    WAITING,
    @SerializedName("confirm")
    CONFIRM,
    @SerializedName("ready")
    READY
}

Przetwarzanie listy z Gsonem

Metoda 1

Gson gson = new Gson();
String json = "[ \"Adam\", \"John\", \"Mary\" ]";

Type type = new TypeToken<List<String>>(){}.getType();
List<String> members = gson.fromJson(json, type);
Log.v("Members", members.toString());

Jest to przydatne w przypadku większości ogólnych klas kontenerów, ponieważ nie można uzyskać klasy sparametryzowanego typu (tzn. Nie można wywołać List<String>.class ).

Metoda 2

public class StringList extends ArrayList<String> { }

...

List<String> members = gson.fromJson(json, StringList.class);

Alternatywnie możesz zawsze podklasować pożądany typ, a następnie przekazać tę klasę. Jednak nie zawsze jest to najlepsza praktyka, ponieważ zwróci ci obiekt typu StringList ;

Serializacja / deserializacja JSON z AutoValue i Gson

Zaimportuj do głównego pliku gradle

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

Zaimportuj do pliku aplikacji gradle

apt 'com.google.auto.value:auto-value:1.2'  
apt 'com.ryanharter.auto.value:auto-value-gson:0.3.1'  
provided 'com.jakewharton.auto.value:auto-value-annotations:1.2-update1'  
provided 'org.glassfish:javax.annotation:10.0-b28'

Utwórz obiekt za pomocą autowartości:

@AutoValue public abstract class SignIn {    
    @SerializedName("signin_token") public abstract String signinToken();
    public abstract String username();

    public static TypeAdapter<SignIn> typeAdapter(Gson gson) {
        return new AutoValue_SignIn.GsonTypeAdapter(gson);
    }

    public static SignIn create(String signin, String username) {
        return new AutoValue_SignIn(signin, username);
    }
}

Utwórz konwerter Gson za pomocą GsonBuilder

Gson gson = new GsonBuilder()
                .registerTypeAdapterFactory(
                    new AutoValueGsonTypeAdapterFactory())
                .create());

Deserializuj

String myJsonData = "{
    \"signin_token\": \"mySigninToken\",
    \"username\": \"myUsername\" }";
SignIn signInData = gson.fromJson(myJsonData, Signin.class);

Serializuj

Signin myData = SignIn.create("myTokenData", "myUsername");
String myJsonData = gson.toJson(myData);

Korzystanie z Gson to świetny sposób na uproszczenie kodu serializacji i deserializacji za pomocą obiektów POJO. Efektem ubocznym jest to, że odbicie jest kosztowne pod względem wydajności. Dlatego użycie AutoValue-Gson do wygenerowania CustomTypeAdapter pozwoli uniknąć tych kosztów odbicia, a jednocześnie będzie bardzo łatwa do aktualizacji, gdy nastąpi zmiana interfejsu API.

Przetwarzanie JSON do ogólnego obiektu klasy za pomocą Gson

Załóżmy, że mamy ciąg JSON :

["first","second","third"]

Możemy parsować ten ciąg JSON w tablicy String :

Gson gson = new Gson();
String jsonArray = "[\"first\",\"second\",\"third\"]";
String[] strings = gson.fromJson(jsonArray, String[].class);

Ale jeśli chcemy parsować go do obiektu List<String> , musimy użyć TypeToken .

Oto próbka:

Gson gson = new Gson();
String jsonArray = "[\"first\",\"second\",\"third\"]";
List<String> stringList = gson.fromJson(jsonArray, new TypeToken<List<String>>() {}.getType());

Załóżmy, że mamy dwie klasy poniżej:

public class Outer<T> {
    public int index;
    public T data;
}

public class Person {
    public String firstName;
    public String lastName;
}

i mamy ciąg JSON, który powinien zostać przeanalizowany do Outer<Person> .

W tym przykładzie pokazano, jak parsować ten ciąg JSON z powiązanym obiektem klasy ogólnej:

String json = "......";
Type userType = new TypeToken<Outer<Person>>(){}.getType();
Result<User> userResult = gson.fromJson(json,userType);

Jeśli ciąg JSON powinien zostać przeanalizowany do Outer<List<Person>> :

Type userListType = new TypeToken<Outer<List<Person>>>(){}.getType();
Result<List<User>> userListResult = gson.fromJson(json,userListType); 

Dodanie Gsona do twojego projektu

dependencies {
    compile 'com.google.code.gson:gson:2.8.1'
}

Aby użyć najnowszej wersji Gson

Poniższy wiersz skompiluje najnowszą wersję biblioteki gson za każdym razem, gdy kompilujesz, nie musisz jej zmieniać.

Plusy: Możesz korzystać z najnowszych funkcji, prędkości i mniej błędów.
Minusy: może to spowodować uszkodzenie kompatybilności z twoim kodem.

compile 'com.google.code.gson:gson:+'

Za pomocą Gson załaduj plik JSON z dysku.

Spowoduje to załadowanie pliku JSON z dysku i przekonwertowanie go na dany typ.

public static <T> T getFile(String fileName, Class<T> type) throws FileNotFoundException {
    Gson gson = new GsonBuilder()
            .create();
    FileReader json = new FileReader(fileName);
    return gson.fromJson(json, type);
}

Dodanie niestandardowego konwertera do Gson

Czasami trzeba serializować lub deserializować niektóre pola w pożądanym formacie, na przykład backend może używać formatu „RRRR-MM-dd GG: mm” dla dat i chcesz, aby POJOS używał klasy DateTime w Joda Time.

Aby automatycznie przekonwertować te ciągi na obiekt DateTimes, możesz użyć niestandardowego konwertera.

/**
 * Gson serialiser/deserialiser for converting Joda {@link DateTime} objects.
 */
public class DateTimeConverter implements JsonSerializer<DateTime>, JsonDeserializer<DateTime> {

    private final DateTimeFormatter dateTimeFormatter;

    @Inject
    public DateTimeConverter() {
        this.dateTimeFormatter = DateTimeFormat.forPattern("YYYY-MM-dd HH:mm");
    }

    @Override
    public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(dateTimeFormatter.print(src));
    }

    @Override
    public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException {
        
        if (json.getAsString() == null || json.getAsString().isEmpty()) {
            return null;
        }

        return dateTimeFormatter.parseDateTime(json.getAsString());
    }
}

Aby Gson mógł korzystać z nowo utworzonego konwertera, musisz przypisać go podczas tworzenia obiektu Gson:

    DateTimeConverter dateTimeConverter = new DateTimeConverter();
    Gson gson = new GsonBuilder().registerTypeAdapter(DateTime.class, dateTimeConverter)
            .create();

    String s = gson.toJson(DateTime.now());
    // this will show the date in the desired format

Aby dokonać deserializacji daty w tym formacie, wystarczy zdefiniować pole w formacie DateTime:

public class SomePojo {
    private DateTime someDate;    
} 

Gdy Gson napotka pole typu DateTime, wywoła konwerter w celu deserializacji tego pola.

Używanie Gson jako serializatora z Retrofit

Przede wszystkim musisz dodać GsonConverterFactory do pliku build.gradle

compile 'com.squareup.retrofit2:converter-gson:2.1.0'

Następnie należy dodać fabrykę konwertera podczas tworzenia usługi modernizacji:

Gson gson = new GsonBuilder().create();
new Retrofit.Builder()
        .baseUrl(someUrl)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build()
        .create(RetrofitService.class);

Możesz dodać niestandardowe konwertery podczas tworzenia obiektu Gson przekazywanego do fabryki. Umożliwiając tworzenie niestandardowych konwersji.

Przetwarzanie tablicy json do klasy ogólnej za pomocą Gson

Załóżmy, że mamy JSON :

{
  "total_count": 132,
  "page_size": 2,
  "page_index": 1,
  "twitter_posts": [
    {
      "created_on": 1465935152,
      "tweet_id": 210462857140252672,
      "tweet": "Along with our new #Twitterbird, we've also updated our Display Guidelines",
      "url": "https://twitter.com/twitterapi/status/210462857140252672"
    },
    {
      "created_on": 1465995741,
      "tweet_id": 735128881808691200,
      "tweet": "Information on the upcoming changes to Tweets is now on the developer site",
      "url": "https://twitter.com/twitterapi/status/735128881808691200"
    }
  ]
}

Możemy parsować tę tablicę do obiektu Niestandardowe tweety (kontener listy tweetów) ręcznie, ale łatwiej jest to zrobić za pomocą metody fromJson :

Gson gson = new Gson();
String jsonArray = "....";
Tweets tweets = gson.fromJson(jsonArray, Tweets.class);

Załóżmy, że mamy dwie klasy poniżej:

class Tweets {
    @SerializedName("total_count")
    int totalCount;
    @SerializedName("page_size")
    int pageSize;
    @SerializedName("page_index")
    int pageIndex;
    // all you need to do it is just define List variable with correct name 
    @SerializedName("twitter_posts")
    List<Tweet> tweets;
}

class Tweet {
    @SerializedName("created_on")
    long createdOn;
    @SerializedName("tweet_id")
    String tweetId;
    @SerializedName("tweet")
    String tweetBody;
    @SerializedName("url")
    String url;
}

a jeśli potrzebujesz po prostu przeanalizować tablicę json, możesz użyć tego kodu podczas analizy:

String tweetsJsonArray = "[{.....},{.....}]"
List<Tweet> tweets = gson.fromJson(tweetsJsonArray, new TypeToken<List<Tweet>>() {}.getType());

Niestandardowy deserializator JSON przy użyciu Gson

Wyobraź sobie, że masz wszystkie daty we wszystkich odpowiedziach w jakimś niestandardowym formacie, na przykład /Date(1465935152)/ i chcesz zastosować ogólną regułę, aby deserializować wszystkie daty Jsona do instancji Date java. W takim przypadku musisz zaimplementować niestandardowy deserializator Json.

Przykład json:

{
  "id": 1,
  "created_on": "Date(1465935152)",
  "updated_on": "Date(1465968945)",
  "name": "Oleksandr"
}

Załóżmy, że mamy tę klasę poniżej:

class User {
    @SerializedName("id")
    long id;
    @SerializedName("created_on")
    Date createdOn;
    @SerializedName("updated_on")
    Date updatedOn;
    @SerializedName("name")
    String name;
}

Niestandardowy deserializer:

class DateDeSerializer implements JsonDeserializer<Date> {
    private static final String DATE_PREFIX = "/Date(";
    private static final String DATE_SUFFIX = ")/";

    @Override
    public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        String dateString = json.getAsString();
        if (dateString.startsWith(DATE_PREFIX) && dateString.endsWith(DATE_SUFFIX)) {
            dateString = dateString.substring(DATE_PREFIX.length(), dateString.length() - DATE_SUFFIX.length());
        } else {
            throw new JsonParseException("Wrong date format: " + dateString);
        }
        return new Date(Long.parseLong(dateString) - TimeZone.getDefault().getRawOffset());
    }
}

A użycie:

Gson gson = new GsonBuilder()
                .registerTypeAdapter(Date.class, new DateDeSerializer())
                .create();
String json = "....";
User user = gson.fromJson(json, User.class);

Serializuj i deserializuj ciągi Jackson JSON z typami Date

Dotyczy to również przypadku, w którym chcesz na przykład dostosować konwersję Gson Date do Jacksona.

Jackson zazwyczaj serializuje Date do „milisekund od epoki”, podczas gdy Gson używa czytelnego formatu, takiego jak Aug 31, 2016 10:26:17 do reprezentowania daty. Prowadzi to do JsonSyntaxExceptions w Gson, gdy próbujesz deserializować datę w formacie Jackson.

Aby to obejść, możesz dodać niestandardowy serializator i niestandardowy deserializator:

JsonSerializer<Date> ser = new JsonSerializer<Date>() {
    @Override
    public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext
                context) {
        return src == null ? null : new JsonPrimitive(src.getTime());
    }
};

JsonDeserializer<Date> deser = new JsonDeserializer<Date>() {
    @Override
    public Date deserialize(JsonElement json, Type typeOfT,
                JsonDeserializationContext context) throws JsonParseException {
        return json == null ? null : new Date(json.getAsLong());
    }
};

Gson gson = new GsonBuilder()
                .registerTypeAdapter(Date.class, ser)
                .registerTypeAdapter(Date.class, deser)
                .create();

Używanie Gson z dziedziczeniem

Gson nie obsługuje dziedziczenia po wyjęciu z pudełka.

Powiedzmy, że mamy następującą hierarchię klas:

public class BaseClass {
    int a;
 
    public int getInt() {
        return a;
   }
}
 
public class DerivedClass1 extends BaseClass {
     int b;
 
     @Override
     public int getInt() {
         return b;
     }
 }
 
public class DerivedClass2 extends BaseClass {
    int c;
 
    @Override
    public int getInt() {
        return c;
    }
}

A teraz chcemy serializować wystąpienie klasy DerivedClass1 do ciągu JSON

DerivedClass1 derivedClass1 = new DerivedClass1();
derivedClass1.b = 5;
derivedClass1.a = 10;
 
Gson gson = new Gson();
String derivedClass1Json = gson.toJson(derivedClass1);

Teraz, w innym miejscu, otrzymujemy ten ciąg json i chcemy go przekształcić z postaci szeregowej - ale w czasie kompilacji wiemy tylko, że ma to być instancja klasy BaseClass :

BaseClass maybeDerivedClass1 = gson.fromJson(derivedClass1Json, BaseClass.class);
System.out.println(maybeDerivedClass1.getInt());

Ale GSON nie wie derivedClass1Json był pierwotnie instancją DerivedClass1 , więc to będzie wydrukować 10.

Jak to rozwiązać?

Musisz zbudować własny JsonDeserializer , który obsługuje takie przypadki. Rozwiązanie nie jest idealnie czyste, ale nie mogłem wymyślić lepszego.

Najpierw dodaj następujące pole do swojej klasy podstawowej

@SerializedName("type")
private String typeName;

I zainicjuj go w konstruktorze klasy podstawowej

public BaseClass() {
    typeName = getClass().getName();
}

Teraz dodaj następującą klasę:

public class JsonDeserializerWithInheritance<T> implements JsonDeserializer<T> {
 
 @Override
 public T deserialize(
     JsonElement json, Type typeOfT, JsonDeserializationContext context)
     throws JsonParseException {
     JsonObject jsonObject = json.getAsJsonObject();
     JsonPrimitive classNamePrimitive = (JsonPrimitive) jsonObject.get("type");
 
     String className = classNamePrimitive.getAsString();
 
     Class<?> clazz;
     try {
     clazz = Class.forName(className);
     } catch (ClassNotFoundException e) {
     throw new JsonParseException(e.getMessage());
     }
     return context.deserialize(jsonObject, clazz);
 }
}

Wszystko, co pozostaje do zrobienia, to podłączyć wszystko -

GsonBuilder builder = new GsonBuilder();
 builder
 .registerTypeAdapter(BaseClass.class, new JsonDeserializerWithInheritance<BaseClass>());
 Gson gson = builder.create();

A teraz uruchamiamy następujący kod

 DerivedClass1 derivedClass1 = new DerivedClass1();
 derivedClass1.b = 5;
 derivedClass1.a = 10;
 String derivedClass1Json = gson.toJson(derivedClass1);
 
 BaseClass maybeDerivedClass1 = gson.fromJson(derivedClass1Json, BaseClass.class);
 System.out.println(maybeDerivedClass1.getInt());

Wydrukuje 5.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow