Модификация метода @Patch (Обновление объекта, который содержит ArrayList) - PullRequest
0 голосов
/ 20 сентября 2019

Примечание: новичок здесь, пожалуйста, дайте мне знать, если мне нужно будет предоставить больше информации или уточнить что-либо.

Чтобы дать вам некоторый контекст: я практикую создание приложения-клона Messenger с большим количеством методов Retrofit,Для этой цели я использую небольшой локальный сервер JSON, с которым связывается приложение.

Когда пользователь приложения создает учетную запись, приложение создает объект профиля на сервере JSON, используя следующий метод:

@FormUrlEncoded
@POST("profiles")
suspend fun createProfile(@Field("username") username: String?,
                            @Field("picture") picture: String?,
                            @Field(value = "nickname") nickname: String?,
                            @Field(value = "contacts") contacts: ArrayList<String?>,
                            @Field(value = "status") status: Int?): Response<Profile>

Изначально ArrayList контактов пуст, поскольку пользователь еще не добавил ни одного контакта.Создание случайного профиля с пустым ArrayList () для параметра контактов, это результат на сервере JSON:

{
    "username": "username.example",
    "picture": "picture's URL",
    "nickname": "Nikola",
    "status": 1,
    "id": 4
}

Класс, представляющий модель профиля внутри приложения, выглядит так:

class Profile(
    val username: String? = "",
    var picture: String? = "",
    var nickname: String? = "",
    var contacts: ArrayList<String?>? = ArrayList(),
    var status: Int? = 1,
    val id: Int? = 0
)

После создания профиля, естественно, пользователь может добавлять новые контакты, что происходит с помощью следующего метода:

@FormUrlEncoded
@PATCH("profiles/{id}")
suspend fun addContact(@Path("id") id: Int?,
                        @Field("contacts") contacts: ArrayList<String?>?): Response<Profile>

И вот где возникает проблема, при самом первом добавленном контакте.ArrayList, который отправляется на сервер, содержит только один элемент, и результат на сервере JSON выглядит следующим образом:

{
    "username": "username.example",
    "picture": "picture's URL",
    "nickname": "Nikola",
    "status": 1,
    "id": 4,
    "contacts": "first.contact"
}

По сути, поскольку массив содержит только один элемент, он сохраняет его как строку.Это создает всевозможные проблемы позже, потому что, как только приложение использует метод @GET для этого профиля, оно ожидает ArrayList для атрибута контактов, но получает строку.

Что я могу сделать, чтобы сделатьпрофиль JSON выглядит следующим образом:

{
    "username": "username.example",
    "picture": "picture's URL",
    "nickname": "Nikola",
    "status": 1,
    "id": 4,
    "contacts": ["first.contact"]
}

Параметр contacts должен быть массивом, даже если в нем есть только один элемент.

1 Ответ

0 голосов
/ 21 сентября 2019

Используйте @Body вместо @Form и @FormUrlEncoded:

data class ProfileContacts(val contacts: List<String>)

@PATCH("profiles/{id}")
suspend fun addContact(@Path("id") id: Int?, @Body contacts: ProfileContacts): Response<Profile>

и добавьте конвертер, если у вас его еще нет, Gson дляпример:

// build.gradle
dependencies {
    implementation 'com.squareup.retrofit2:converter-gson:2.6.1' // latest version
}
// Retrofit Builder
val retrofit = Retrofit.Builder()
    ... // other methods
    .addConverterFactory(GsonConverterFactory.create())
    .build()

@Body позволяет определить тело запроса как класс Kotlin, который в конечном итоге будет сериализован с использованием предоставленного Converter (в случае Gson он будет преобразован вJSON).@Field, с другой стороны, используется для отправки данных как application/x-www-form-urlencoded (как и предполагает обязательная аннотация @FormUrlEncoded).Это означает, что тело вашего запроса будет закодировано в список пар ключ-значение, разделенных символом &, например (на основе метода createProfile):

username=username.example&picture=picture%27s%20URL&nickname=Nikola&status=1&id=4

Вы можете POSTмассив как application/x-www-form-urlencoded, используя один и тот же ключ более одного раза.Вот что в основном происходит, когда вы аннотируете список с помощью аннотации Retrofit @Field - каждый элемент списка связан с общим ключом, например:

@FormUrlEncoded
@PATCH("profiles/{id}")
suspend fun addContact(@Path("id") id: Int?,
                       @Field("contacts") contacts: ArrayList<String?>?): Response<Profile>

// ...
addContact(1, arrayListOf("first.contact", "second.contact"))

// request body:
contacts=first.contact&contacts=second.contact

Поэтому, когда вы пытаетесь обновить профиль, используя толькосписок из одного элемента contacts, создается одна пара «контактов» (contacts=first.contact) и обрабатывается как строковое значение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...