Android
Gson
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.