В статье рассказывается:
- Сериализация от Kotlin
- Сериализация от Kotlin + Retrofit
-
Пройди тест и узнай, какая сфера тебе подходит:
айти, дизайн или маркетинг.Бесплатно от Geekbrains
Любой Android-разработчик рано или поздно сталкивается с форматом представления данных типа JSON. Наиболее часто он используется для передачи/получения данных с какого-либо сервера. Формат предельно прост, подробнее о нём можно почитать в Википедии:
«JSON (JavaScript Object Notation, обычно произносится как /ˈdʒeɪsən/ JAY-sən) — текстовый формат обмена данными, основанный на JavaScript. Как и многие другие текстовые форматы, JSON легко читается людьми. Несмотря на происхождение от JavaScript, формат считается независимым от языка и может использоваться практически с любым языком программирования. Для многих языков существует готовый код для создания и обработки данных в формате JSON».
входят в ТОП-30 с доходом
от 210 000 ₽/мес
Скачивайте и используйте уже сегодня:
Топ-30 самых востребованных и высокооплачиваемых профессий 2023
Поможет разобраться в актуальной ситуации на рынке труда
Подборка 50+ бесплатных нейросетей для упрощения работы и увеличения заработка
Только проверенные нейросети с доступом из России и свободным использованием
ТОП-100 площадок для поиска работы от GeekBrains
Список проверенных ресурсов реальных вакансий с доходом от 210 000 ₽
Любой класс в 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, но они не подставляются, а код всё равно работает. Вот так неожиданность! Не такого поведения мы ожидали!
на обучение «Android-разработчик» до 08 декабря
Давайте разбираться. Дело в том, что, как мы писали выше, библиотека 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-разработчиков:
- «Эксперименты с новыми API Android 12: Render Effect»
- «Android 12: пристальный разбор главных фич»
- «Эксперименты с новыми API Android 12: Splash Screen»
- «Гайд по отладке Android-приложения: ищем баги и читаем логи»
А если затянет — приходите на факультет Android-разработки. В время учебы вы разработаете Android-приложение и выложите его в Google Play, даже если никогда не программировали. А также своите языки Java и Kotlin, командную разработку, Material Design и принципы тестирования.