Поиск…


Вступление

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.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow