Android
Gson
Zoeken…
Invoering
Gson is een Java-bibliotheek die kan worden gebruikt om Java-objecten om te zetten in hun JSON-weergave. Gson beschouwt deze beide als zeer belangrijke ontwerpdoelen.
Gson-kenmerken:
Lever eenvoudige toJson()
en fromJson()
methoden om Java-objecten naar JSON te converteren en vice versa
Sta toe dat reeds bestaande niet-wijzigbare objecten naar en van JSON worden geconverteerd
Uitgebreide ondersteuning van Java Generics
Ondersteuning van willekeurig complexe objecten (met diepe overervinghiërarchieën en uitgebreid gebruik van generieke typen)
Syntaxis
- Excluder excluder ()
- FieldNamingStrategy fieldNamingStrategy ()
- <T> T fromJson (JsonElement json, Class <T> classOfT)
- <T> T fromJson (JsonElement json, Type typeOfT)
- <T> T fromJson (JsonReader reader, Type 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 (Klasse <T> type)
- <T> TypeAdapter <T> getAdapter (TypeToken <T> type)
- <T> TypeAdapter <T> getDelegateAdapter (TypeAdapterFactory skipPast, TypeToken <T> type)
- JsonReader nieuwJsonReader (Reader reader)
- JsonWriter newJsonWriter (Writer writer)
- 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, JsonWriter writer)
- void toJson (Object src, Appendable writer)
JSON parseren met Gson
Het voorbeeld toont het parseren van een JSON-object met behulp van de Gson-bibliotheek van Google .
Objecten parseren:
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 = "";
}
}
Gebruik vervolgens het volgende wanneer parseren moet plaatsvinden:
String robotJson = "{
\"version\": \"JellyBean\",
\"age\": 3,
\"robotName\": \"Droid\"
}";
Gson gson = new Gson();
Robot robot = gson.fromJson(robotJson, Robot.class);
Een lijst parseren:
Wanneer u een lijst met JSON-objecten ophaalt, wilt u deze vaak parseren en converteren naar Java-objecten.
De JSON-string die we zullen proberen te converteren is de volgende:
{
"owned_dogs": [
{
"name": "Ron",
"age": 12,
"breed": "terrier"
},
{
"name": "Bob",
"age": 4,
"breed": "bulldog"
},
{
"name": "Johny",
"age": 3,
"breed": "golden retriever"
}
]
}
Deze specifieke JSON-array bevat drie objecten. In onze Java-code willen we deze objecten toewijzen aan Dog
objecten. Een object Dog ziet er zo uit:
private class Dog {
public String name;
public int age;
@SerializedName("breed")
public String breedName;
}
Om de JSON-array te converteren naar een Dog[]
:
Dog[] arrayOfDogs = gson.fromJson(jsonArrayString, Dog[].class);
Een Dog[]
converteren naar een JSON-reeks:
String jsonArray = gson.toJson(arrayOfDogs, Dog[].class);
Om de JSON-array te converteren naar een ArrayList<Dog>
kunnen we het volgende doen:
Type typeListOfDogs = new TypeToken<List<Dog>>(){}.getType();
List<Dog> listOfDogs = gson.fromJson(jsonArrayString, typeListOfDogs);
Het Type object typeListOfDogs
definieert hoe een lijst met Dog
objecten eruit zou zien. GSON kan dit type object gebruiken om de JSON-array aan de juiste waarden toe te wijzen.
Als alternatief kan een List<Dog>
naar een JSON-array worden omgezet op een vergelijkbare manier.
String jsonArray = gson.toJson(listOfDogs, typeListOfDogs);
JSON-eigenschap parseren voor Gson
Als je een string wilt parsen om met Gson op te tellen:
{"status": "open"}
public enum Status {
@SerializedName("open")
OPEN,
@SerializedName("waiting")
WAITING,
@SerializedName("confirm")
CONFIRM,
@SerializedName("ready")
READY
}
Een lijst parseren met Gson
Methode 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());
Dit is handig voor de meeste generieke containerklassen, omdat u de klasse van een geparametriseerd type niet kunt List<String>.class
(dat wil zeggen: u kunt List<String>.class
niet aanroepen).
Methode 2
public class StringList extends ArrayList<String> { }
...
List<String> members = gson.fromJson(json, StringList.class);
Als alternatief kunt u altijd het gewenste type subklasse maken en vervolgens die klasse doorgeven. Dit is echter niet altijd de beste StringList
, omdat het een object van het type StringList
zal retourneren;
JSON Serialisatie / Deserialisatie met AutoValue en Gson
Importeer in je gradle rootbestand
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
Importeer in uw gradle-app-bestand
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'
Maak een object met autovalue:
@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);
}
}
Maak uw Gson-omzetter met uw GsonBuilder
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(
new AutoValueGsonTypeAdapterFactory())
.create());
deserialize
String myJsonData = "{
\"signin_token\": \"mySigninToken\",
\"username\": \"myUsername\" }";
SignIn signInData = gson.fromJson(myJsonData, Signin.class);
serialize
Signin myData = SignIn.create("myTokenData", "myUsername");
String myJsonData = gson.toJson(myData);
Het gebruik van Gson is een geweldige manier om de code voor serialisatie en deserialisatie te vereenvoudigen met behulp van POJO-objecten. Het neveneffect is dat reflectie qua prestaties kostbaar is. Dat is de reden waarom het gebruik van AutoValue-Gson om CustomTypeAdapter te genereren, deze reflectiekosten voorkomt en zeer eenvoudig te updaten blijft wanneer er een API-verandering plaatsvindt.
JSON parseren naar Generic Class Object met Gson
Stel dat we een JSON-string hebben :
["first","second","third"]
We kunnen deze JSON-string ontleden in een String
array:
Gson gson = new Gson();
String jsonArray = "[\"first\",\"second\",\"third\"]";
String[] strings = gson.fromJson(jsonArray, String[].class);
Maar als we het in een List<String>
-object willen parseren, moeten we TypeToken
.
Hier is het voorbeeld:
Gson gson = new Gson();
String jsonArray = "[\"first\",\"second\",\"third\"]";
List<String> stringList = gson.fromJson(jsonArray, new TypeToken<List<String>>() {}.getType());
Stel dat we hieronder twee klassen hebben:
public class Outer<T> {
public int index;
public T data;
}
public class Person {
public String firstName;
public String lastName;
}
en we hebben een JSON-string die moet worden geparseerd naar een Outer<Person>
-object.
Dit voorbeeld laat zien hoe u deze JSON-reeks kunt parseren naar het gerelateerde generieke klasseobject:
String json = "......";
Type userType = new TypeToken<Outer<Person>>(){}.getType();
Result<User> userResult = gson.fromJson(json,userType);
Als de JSON-string moet worden geparseerd naar een Outer<List<Person>>
-object:
Type userListType = new TypeToken<Outer<List<Person>>>(){}.getType();
Result<List<User>> userListResult = gson.fromJson(json,userListType);
Gson toevoegen aan uw project
dependencies {
compile 'com.google.code.gson:gson:2.8.1'
}
Om de nieuwste versie van Gson te gebruiken
De onderstaande regel compileert de nieuwste versie van gson library telkens wanneer u compileert, u hoeft de versie niet te wijzigen.
Voordelen: u kunt de nieuwste functies, snelheid en minder bugs gebruiken.
Nadelen: het kan de compatibiliteit met uw code verbreken.
compile 'com.google.code.gson:gson:+'
Gson gebruiken om een JSON-bestand van schijf te laden.
Dit laadt een JSON-bestand van schijf en converteert het naar het gegeven type.
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);
}
Een aangepaste converter toevoegen aan Gson
Soms moet u sommige velden in een gewenst formaat serialiseren of deserialiseren, bijvoorbeeld uw backend kan het formaat "JJJJ-MM-dd HH: mm" gebruiken voor datums en u wilt dat uw POJOS de DateTime-klasse in Joda Time gebruikt.
Om deze strings automatisch te converteren naar DateTimes-object, kunt u een aangepaste converter gebruiken.
/**
* 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());
}
}
Om Gson de nieuw gemaakte converter te laten gebruiken, moet u deze toewijzen bij het maken van het Gson-object:
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
Om de datum in dat formaat te deserialiseren, hoeft u alleen een veld in het DateTime-formaat te definiëren:
public class SomePojo {
private DateTime someDate;
}
Wanneer Gson een veld van het type DateTime tegenkomt, zal het uw converter aanroepen om het veld te deserialiseren.
Gson gebruiken als serializer met Retrofit
Allereerst moet u de GsonConverterFactory
aan uw build.gradle-bestand
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
Vervolgens moet u de converterfabriek toevoegen bij het maken van de Retrofit-service:
Gson gson = new GsonBuilder().create();
new Retrofit.Builder()
.baseUrl(someUrl)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(RetrofitService.class);
U kunt aangepaste converters toevoegen wanneer u het Gson-object maakt dat u doorgeeft aan de fabriek. Hiermee kunt u aangepaste type conversies maken.
Json-array met Gson naar generieke klasse parseren
Stel dat we een json hebben :
{
"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"
}
]
}
We kunnen deze array handmatig in een Custom Tweets (tweets list container) parseren, maar het is gemakkelijker om het te doen met de fromJson
methode:
Gson gson = new Gson();
String jsonArray = "....";
Tweets tweets = gson.fromJson(jsonArray, Tweets.class);
Stel dat we hieronder twee klassen hebben:
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;
}
en als je gewoon een json-array moet parseren, kun je deze code gebruiken bij het parseren:
String tweetsJsonArray = "[{.....},{.....}]"
List<Tweet> tweets = gson.fromJson(tweetsJsonArray, new TypeToken<List<Tweet>>() {}.getType());
Aangepaste JSON Deserializer met behulp van Gson
Stel dat u alle datums in alle antwoorden in een aangepast formaat hebt, bijvoorbeeld /Date(1465935152)/
en dat u een algemene regel wilt toepassen om alle Json-datums te deserialiseren naar java Date
instanties. In dit geval moet u aangepaste Json Deserializer implementeren.
Voorbeeld van json:
{
"id": 1,
"created_on": "Date(1465935152)",
"updated_on": "Date(1465968945)",
"name": "Oleksandr"
}
Stel dat we deze klasse hieronder hebben:
class User {
@SerializedName("id")
long id;
@SerializedName("created_on")
Date createdOn;
@SerializedName("updated_on")
Date updatedOn;
@SerializedName("name")
String name;
}
Aangepaste 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());
}
}
En het gebruik:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Date.class, new DateDeSerializer())
.create();
String json = "....";
User user = gson.fromJson(json, User.class);
Serialiseer en deserialiseer Jackson JSON-strings met datumtypen
Dit geldt ook voor het geval waarin u Gson Date-conversie bijvoorbeeld compatibel wilt maken met Jackson.
Jackson rangschikt datum meestal in "milliseconden sinds het tijdperk", terwijl Gson een leesbaar formaat zoals Aug 31, 2016 10:26:17
om Datum te vertegenwoordigen. Dit leidt tot JsonSyntaxExceptions in Gson wanneer u probeert een Jackson-formaat Datum te deserialiseren.
Om dit te omzeilen, kunt u een aangepaste serializer en een aangepaste deserializer toevoegen:
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();
Gson gebruiken met overerving
Gson ondersteunt out-of-the-box geen erfenis.
Laten we zeggen dat we de volgende klassenhiërarchie hebben:
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;
}
}
En nu willen we een exemplaar van DerivedClass1
naar een JSON-string
DerivedClass1 derivedClass1 = new DerivedClass1();
derivedClass1.b = 5;
derivedClass1.a = 10;
Gson gson = new Gson();
String derivedClass1Json = gson.toJson(derivedClass1);
Nu, op een andere plaats, ontvangen we deze json-string en willen we deze deserialiseren - maar tijdens het compileren weten we alleen dat het een instantie van BaseClass
:
BaseClass maybeDerivedClass1 = gson.fromJson(derivedClass1Json, BaseClass.class);
System.out.println(maybeDerivedClass1.getInt());
Maar GSON weet niet dat derivedClass1Json
oorspronkelijk een instantie was van DerivedClass1
, dus dit zal 10 afdrukken.
Hoe dit op te lossen?
U moet uw eigen JsonDeserializer
bouwen, die dergelijke gevallen afhandelt. De oplossing is niet perfect schoon, maar ik kon geen betere bedenken.
Voeg eerst het volgende veld toe aan uw basisklasse
@SerializedName("type")
private String typeName;
En initialiseer het in de constructeur van de basisklasse
public BaseClass() {
typeName = getClass().getName();
}
Voeg nu de volgende klasse toe:
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);
}
}
U hoeft alleen nog maar alles aan te sluiten -
GsonBuilder builder = new GsonBuilder();
builder
.registerTypeAdapter(BaseClass.class, new JsonDeserializerWithInheritance<BaseClass>());
Gson gson = builder.create();
En nu, met de volgende code-
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());
Wordt afgedrukt 5.