На информационном ресурсе применяются рекомендательные технологии (информационные технологии предоставления информации на основе сбора, систематизации и анализа сведений, относящихся к предпочтениям пользователей сети "Интернет", находящихся на территории Российской Федерации)

GeekBrains

4 подписчика

Android: парсим JSON правильно

Любой Android-разработчик рано или поздно сталкивается с форматом представления данных типа JSON. Наиболее часто он используется для передачи/получения данных с какого-либо сервера. Формат предельно прост, подробнее о нём можно почитать в Википедии:

«JSON (JavaScript Object Notation, обычно произносится как /ˈdʒeɪsən/ JAY-sən) — текстовый формат обмена данными, основанный на JavaScript.

Как и многие другие текстовые форматы, JSON легко читается людьми. Несмотря на происхождение от JavaScript, формат считается независимым от языка и может использоваться практически с любым языком программирования. Для многих языков существует готовый код для создания и обработки данных в формате JSON».

Любой класс в Java или Kotlin можно представить в виде структуры JSON, где есть поля, атрибуты, фигурные скобки обозначают объект, квадратные скобки — массив.

Следующий пример показывает JSON-представление данных об объекте, описывающем человека. В данных присутствуют строковые поля имени и фамилии, информация об адресе и массив, содержащий список телефонов. Как видно из примера, значение может представлять собой вложенную структуру:

 {    "firstName": "Иван",    "lastName": "Иванов",    "address": {        "streetAddress": "Московское ш., 101, кв.101",        "city": "Ленинград",        "postalCode": 101101    },    "phoneNumbers": [        "812 123-1234",        "916 123-4567"    ] }  

Наиболее популярный среди разработчиков способ трансформировать данные в JSON и обратно — это библиотека GSON от самих разработчиков Google. Она очень проста в использовании, мало весит и интегрирована во многие библиотеки.

 

На данный момент все, за редким исключением, Android-разработчики используют Kotlin, а GSON (как и другие подобные библиотеки типа Jackson или Moshi) написана на Java. Это не страшно, потому что Kotlin и Java полностью взаимозаменяемы, но есть небольшие нюансы, которые могут привести к совершенно неожиданным результатам.

Давайте создадим класс User и посмотрим на эти нюансы на практике. В этом классе у нас будут обычные поля и поля со значениями по умолчанию. Как вы знаете, в Java нельзя присваивать переменным значения по умолчанию, а в Kotlin можно:

 data class User(     val name: String,     val email: String,     val age: Int = 13,     val role: Role = Role.Viewer )   enum class Role { Viewer, Editor, Owner }

И теперь представим, что с какого-то сервера пришли данные о пользователе в формате JSON:

 {    "name" : "John Doe",    "email" : "john.doe@email.com" }

Теперь нам нужно распарсить этот JSON и превратить его в обычный класс Kotlin с помощью библиотеки GSON. Добавим зависимость GSON в наш проект в файл Gradle

implementation 'com.google.code.gson:gson:2.8.6'

и сразу напишем тест:

 class JsonUnitTest {       private val jsonString = """             {                 "name" : "John Doe",                 "email" : "john.doe@email.com"             }         """       @Test     fun gsonTest() {         val user = Gson().fromJson(jsonString, User::class.java)           assertEquals("John Doe",user.name)         assertEquals(null, user.role)         assertEquals(0, user.age)         //User(name=John Doe, email=john.doe@email.com, age=0, role=null)     } }

Тест прекрасно выполняется без единой ошибки, то есть код работает. Но обратите внимание, что возраст пользователя у нас == 0, а его роль не определена, хотя в самом классе у нас прописаны значения по умолчанию для этих переменных. Если эти параметры не определены в JSON, должны подставляться значения по умолчанию: возраст == 13, а роль == Viewer, но они не подставляются, а код всё равно работает. Вот так неожиданность! Не такого поведения мы ожидали!

Давайте разбираться. Дело в том, что, как мы писали выше, библиотека GSON написана на Java, а это значит, что значения по умолчанию для несуществующих полей такие: для примитива int — это 0, для отсутствующего объекта — это null. Простая трансформация JSON в класс на Kotlin может легко сломать null-safety, на который так рассчитывают все разработчики, и может привести к падению приложения там, где оно падать не должно. 

И тут нам на помощь приходит котлиновская библиотека по сериализации объектов.

Сериализация от Kotlin

Это небольшая вспомогательная библиотечка от разработчиков языка, которая работает с помощью аннотации @Serializable. С ней у вас не будет проблем при использовании полей по умолчанию. Чтобы подключить библиотеку к своему проекту, нужно прописать в файле Gradle плагин и несколько зависимостей. В файле проекта build.gradle(Project):

 buildscript {    repositories {        google()        mavenCentral()    }    dependencies {        classpath "com.android.tools.build:gradle:7.0.2"        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31"        classpath "org.jetbrains.kotlin:kotlin-serialization:1.5.31"    } }

В файле проекта build.gradle(Module:app):

 plugins {    id 'com.android.application'    id 'kotlin-android'    id 'kotlinx-serialization' }   dependencies {     implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0" }

Теперь трансформация JSON (сериализация) будет проходить корректно. Напишем наш класс и добавим аннотацию:

 @Serializable data class User(     val name: String,     val email: String,     val age: Int = 13,     val role: Role = Role.Viewer )   enum class Role { Viewer, Editor, Owner }

Протестируем:

 class JsonUnitTest {       private val jsonString = """             {                 "name" : "John Doe",                 "email" : "john.doe@email.com"             }         """.trimIndent()       @Test     fun gsonTest() {         val user = Gson().fromJson(jsonString, User::class.java)           assertEquals("John Doe", user.name)         assertEquals(null, user.role)         assertEquals(0, user.age)         //User(name=John Doe, email=john.doe@email.com, age=0, role=null)     }       @Test     fun jsonTest() {         val user = Json.parse(User.serializer(), jsonString)           assertEquals("John Doe", user.name)         assertEquals(Role.Viewer, user.role)         assertEquals(13, user.age)        //User(name=John Doe, email=john.doe@email.com, age=13, role=Viewer)     } }

Тест пройден успешно! Теперь класс сериализуется у нас со значениями по умолчанию, если таковые прописаны в классе.

Сериализация от Kotlin + Retrofit

Если вы хоть раз отправляли запрос на сервер или получали с сервера какой-то ответ, то наверняка вы знакомы с библиотекой Retrofit. В этой библиотеке нет поддержки сериализации от Kotlin, но у вас есть возможность добавить вспомогательную библиотеку от Джека Вортона в качестве зависимости Gradle:

 dependencies { //Retrofit implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0' implementation 'com.squareup.okhttp3:okhttp:4.9.1' }

Теперь при использовании Retrofit сериализация будет происходить автоматически:

 val contentType = "application/json".toMediaType() val retrofit = Retrofit.Builder() .baseUrl("https://www.example.com") .addConverterFactory(Json(JsonConfiguration(strictMode = false)).asConverterFactory(contentType)) .build()

Дополнительно, но не обязательно вы можете использовать JsonConfiguration для выключения StrictMode. StrictMode включен по умолчанию и запрещает использование неизвестных ключей в JSON и нечисловые значения в числах с плавающей точкой. Хорошая практика — включать StrictMode в «дебажной» версии приложения и выключать его в «релизной».

Читайте больше полезных статей для начинающих Android-разработчиков:

А если затянет — приходите на факультет Android-разработки. В время учебы вы разработаете Android-приложение и выложите его в Google Play, даже если никогда не программировали. А также своите языки Java и Kotlin, командную разработку, Material Design и принципы тестирования.

 

Ссылка на первоисточник
наверх