Android
Gson
Поиск…
Вступление
Gson - это библиотека Java, которая может быть использована для преобразования объектов Java в их представление JSON. Г-н считает эти цели очень важными.
Особенности Gson:
Предоставьте простые toJson()
и fromJson()
для преобразования объектов Java в JSON и наоборот
Разрешить преобразование ранее немодифицируемых объектов в JSON и обратно
Обширная поддержка Java Generics
Поддержка произвольно сложных объектов (с глубокими иерархиями наследования и широким использованием родовых типов)
Синтаксис
- Исключительное исключение ()
- Поле FieldNamingStrategyNamingStrategy ()
- <T> T fromJson (JsonElement json, класс <T> classOfT)
- <T> T fromJson (JsonElement json, Тип typeOfT)
- <T> T от Json (читатель JsonReader, тип typeOfT)
- <T> T fromJson (Reader json, Class <T> classOfT)
- <T> T fromJson (Reader json, Тип typeOfT)
- <T> T fromJson (String json, Class <T> classOfT)
- <T> T fromJson (String json, Тип typeOfT)
- <T> Тип Adapter <T> getAdapter (тип <T>)
- <T> TypeAdapter <T> getAdapter (тип TypeToken <T>)
- <T> TypeAdapter <T> getDelegateAdapter (TypeAdapterFactory skipPast, TypeToken <T>)
- JsonReader newJsonReader (считыватель чтения)
- JsonWriter newJsonWriter (писатель-писатель)
- JsonElement toJsonTree (Object src)
- JsonElement toJsonTree (объект src, тип typeOfSrc)
- boolean serializeNulls ()
- boolean htmlSafe ()
- Строка toJson (JsonElement jsonElement)
- Строка toJson (Object src)
- Строка toJson (Object src, Тип typeOfSrc)
- Строка toString ()
- void toJson (Object src, Тип typeOfSrc, Добавочный писатель)
- void toJson (Object src, Тип typeOfSrc, JsonWriter writer)
- void toJson (JsonElement jsonElement, Appendable writer)
- void toJson (JsonElement jsonElement, автор JsonWriter)
- void toJson (Object src, Appendable writer)
Разбор JSON с Gson
В этом примере показан разбор объекта JSON с использованием библиотеки Gson от Google .
Разбор объектов:
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 = "";
}
}
Затем, когда необходимо провести синтаксический анализ, используйте следующее:
String robotJson = "{
\"version\": \"JellyBean\",
\"age\": 3,
\"robotName\": \"Droid\"
}";
Gson gson = new Gson();
Robot robot = gson.fromJson(robotJson, Robot.class);
Разбор списка:
Когда вы извлекаете список объектов JSON, вам часто приходится анализировать их и преобразовывать в объекты Java.
Строка JSON, которую мы попытаемся преобразовать, следующая:
{
"owned_dogs": [
{
"name": "Ron",
"age": 12,
"breed": "terrier"
},
{
"name": "Bob",
"age": 4,
"breed": "bulldog"
},
{
"name": "Johny",
"age": 3,
"breed": "golden retriever"
}
]
}
Этот конкретный массив JSON содержит три объекта. В нашем Java-коде мы хотим сопоставить эти объекты с объектами Dog
. Объект Dog будет выглядеть так:
private class Dog {
public String name;
public int age;
@SerializedName("breed")
public String breedName;
}
Чтобы преобразовать массив JSON в Dog[]
:
Dog[] arrayOfDogs = gson.fromJson(jsonArrayString, Dog[].class);
Преобразование Dog[]
в строку JSON:
String jsonArray = gson.toJson(arrayOfDogs, Dog[].class);
Чтобы преобразовать массив JSON в ArrayList<Dog>
мы можем сделать следующее:
Type typeListOfDogs = new TypeToken<List<Dog>>(){}.getType();
List<Dog> listOfDogs = gson.fromJson(jsonArrayString, typeListOfDogs);
Тип объекта typeListOfDogs
определяет, как будет выглядеть список объектов Dog
. GSON может использовать этот объект типа для сопоставления массива JSON с правильными значениями.
Альтернативно, преобразование List<Dog>
в массив JSON может быть выполнено аналогичным образом.
String jsonArray = gson.toJson(listOfDogs, typeListOfDogs);
Разбор свойств JSON для перечисления с помощью Gson
Если вы хотите разобрать String для перечисления с помощью Gson:
{"status": "open"}
public enum Status {
@SerializedName("open")
OPEN,
@SerializedName("waiting")
WAITING,
@SerializedName("confirm")
CONFIRM,
@SerializedName("ready")
READY
}
Разбор списка с Гсоном
Способ 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());
Это полезно для большинства общих классов контейнеров, поскольку вы не можете получить класс параметризованного типа (т. Е. Вы не можете вызвать List<String>.class
).
Способ 2
public class StringList extends ArrayList<String> { }
...
List<String> members = gson.fromJson(json, StringList.class);
Кроме того, вы всегда можете подклассифицировать нужный тип, а затем пройти в этом классе. Однако это не всегда лучшая практика, так как она вернет вам объект типа StringList
;
Сериализация / десериализация JSON с помощью AutoValue и Gson
Импортировать в корневой файл gradle
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
Импорт в файл приложения 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'
Создать объект с 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);
}
}
Создайте свой конвертер Gson с помощью вашего GsonBuilder
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(
new AutoValueGsonTypeAdapterFactory())
.create());
Deserialize
String myJsonData = "{
\"signin_token\": \"mySigninToken\",
\"username\": \"myUsername\" }";
SignIn signInData = gson.fromJson(myJsonData, Signin.class);
Сериализация
Signin myData = SignIn.create("myTokenData", "myUsername");
String myJsonData = gson.toJson(myData);
Использование Gson - отличный способ упростить код Serialization и Deserialization, используя объекты POJO. Побочным эффектом является то, что отражение является дорогостоящим. Вот почему использование AutoValue-Gson для генерации CustomTypeAdapter позволит избежать этой стоимости отражения, оставаясь очень простым для обновления при изменении api.
Разбор JSON для универсального класса с Gson
Предположим, что у нас есть строка JSON:
["first","second","third"]
Мы можем проанализировать эту строку JSON в массив String
:
Gson gson = new Gson();
String jsonArray = "[\"first\",\"second\",\"third\"]";
String[] strings = gson.fromJson(jsonArray, String[].class);
Но если мы хотим разобрать его в объект List<String>
, мы должны использовать TypeToken
.
Вот пример:
Gson gson = new Gson();
String jsonArray = "[\"first\",\"second\",\"third\"]";
List<String> stringList = gson.fromJson(jsonArray, new TypeToken<List<String>>() {}.getType());
Предположим, что у нас есть два класса:
public class Outer<T> {
public int index;
public T data;
}
public class Person {
public String firstName;
public String lastName;
}
и у нас есть строка JSON, которая должна анализироваться на объект Outer<Person>
.
В этом примере показано, как разбить эту строку JSON на связанный общий объект класса:
String json = "......";
Type userType = new TypeToken<Outer<Person>>(){}.getType();
Result<User> userResult = gson.fromJson(json,userType);
Если строка JSON должна анализироваться на объект Outer<List<Person>>
:
Type userListType = new TypeToken<Outer<List<Person>>>(){}.getType();
Result<List<User>> userListResult = gson.fromJson(json,userListType);
Добавление Gson к вашему проекту
dependencies {
compile 'com.google.code.gson:gson:2.8.1'
}
Использовать последнюю версию Gson
Следующая строка будет компилировать последнюю версию gson-библиотеки каждый раз, когда вы компилируете, вам не нужно менять версию.
Плюсы: вы можете использовать новейшие функции, скорость и меньше ошибок.
Минусы: он может нарушить совместимость с вашим кодом.
compile 'com.google.code.gson:gson:+'
Использование Gson для загрузки JSON-файла с диска.
Это загрузит JSON-файл с диска и преобразует его в заданный тип.
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);
}
Добавление пользовательского конвертера в Gson
Иногда вам нужно сериализовать или десериализовать некоторые поля в нужном формате, например, ваш бэкэнд может использовать формат «YYYY-MM-dd HH: mm» для дат, и вы хотите, чтобы ваш POJOS использовал класс DateTime в Joda Time.
Чтобы автоматически преобразовать эти строки в объект DateTimes, вы можете использовать собственный конвертер.
/**
* 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());
}
}
Чтобы заставить Gson использовать вновь созданный конвертер, вам нужно назначить его при создании объекта 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
Чтобы десериализовать дату в этом формате, вам нужно определить поле в формате DateTime:
public class SomePojo {
private DateTime someDate;
}
Когда Gson встречает поле типа DateTime, он вызовет ваш конвертер, чтобы десериализовать поле.
Использование Gson в качестве сериализатора с дооснащением
Прежде всего вам нужно добавить GsonConverterFactory
в файл build.gradle
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
Затем вы должны добавить фабрику преобразователя при создании Retrofit Service:
Gson gson = new GsonBuilder().create();
new Retrofit.Builder()
.baseUrl(someUrl)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(RetrofitService.class);
Вы можете добавить собственные преобразователи при создании объекта Gson, который вы передаете на заводе. Позволяет создавать персонализированные преобразования типов.
Разбор массива json для универсального класса с использованием Gson
Предположим, что у нас есть 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"
}
]
}
Мы можем проанализировать этот массив в пользовательский твиты (содержимое списка твитов), но это проще сделать с fromJson
метода fromJson
:
Gson gson = new Gson();
String jsonArray = "....";
Tweets tweets = gson.fromJson(jsonArray, Tweets.class);
Предположим, что у нас есть два класса:
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;
}
и если вам нужно просто разобрать json-массив, вы можете использовать этот код в своем разборе:
String tweetsJsonArray = "[{.....},{.....}]"
List<Tweet> tweets = gson.fromJson(tweetsJsonArray, new TypeToken<List<Tweet>>() {}.getType());
Пользовательский десериализатор JSON с использованием Gson
Представьте, что у вас есть все даты во всех ответах в каком-то специальном формате, например /Date(1465935152)/
и вы хотите применить общее правило для десериализации всех дат Json для экземпляров java Date
. В этом случае вам нужно реализовать пользовательский Json Deserializer.
Пример json:
{
"id": 1,
"created_on": "Date(1465935152)",
"updated_on": "Date(1465968945)",
"name": "Oleksandr"
}
Предположим, что этот класс ниже:
class User {
@SerializedName("id")
long id;
@SerializedName("created_on")
Date createdOn;
@SerializedName("updated_on")
Date updatedOn;
@SerializedName("name")
String name;
}
Пользовательский десериализатор:
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());
}
}
И использование:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Date.class, new DateDeSerializer())
.create();
String json = "....";
User user = gson.fromJson(json, User.class);
Сериализация и десериализация строк Jackson JSON с типами даты
Это также относится к случаю, когда вы хотите сделать преобразование Gson Date совместимым с Jackson, например.
Джексон обычно сериализует Date на «миллисекунды с эпохи», тогда как Gson использует читаемый формат, такой как Aug 31, 2016 10:26:17
для представления Date. Это приводит к JsonSyntaxExceptions в Gson, когда вы пытаетесь десериализовать формат даты в формате Jackson.
Чтобы обойти это, вы можете добавить собственный сериализатор и собственный десериализатор:
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 с наследованием
Gson не поддерживает наследование из коробки.
Допустим, у нас есть следующая иерархия классов:
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;
}
}
И теперь мы хотим сериализовать экземпляр DerivedClass1
в строку JSON
DerivedClass1 derivedClass1 = new DerivedClass1();
derivedClass1.b = 5;
derivedClass1.a = 10;
Gson gson = new Gson();
String derivedClass1Json = gson.toJson(derivedClass1);
Теперь, в другом месте, мы получаем эту строку json и хотим ее десериализовать, но во время компиляции мы знаем только, что это должен быть экземпляр BaseClass
:
BaseClass maybeDerivedClass1 = gson.fromJson(derivedClass1Json, BaseClass.class);
System.out.println(maybeDerivedClass1.getInt());
Но GSON не знает, что derivedClass1Json
был первоначально экземпляром DerivedClass1
, поэтому он будет распечатывать 10.
Как это решить?
Вам нужно создать собственный JsonDeserializer
, который обрабатывает такие случаи. Решение не совсем чистое, но я не мог придумать лучшего.
Сначала добавьте следующее поле в базовый класс
@SerializedName("type")
private String typeName;
И инициализируйте его в конструкторе базового класса
public BaseClass() {
typeName = getClass().getName();
}
Теперь добавьте следующий класс:
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);
}
}
Все, что осталось сделать, это все перехватить -
GsonBuilder builder = new GsonBuilder();
builder
.registerTypeAdapter(BaseClass.class, new JsonDeserializerWithInheritance<BaseClass>());
Gson gson = builder.create();
И теперь, запустив следующий код,
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());
Распечатает 5.