Модификация - добавление поля динамически во время сериализации - PullRequest
0 голосов
/ 07 ноября 2018

В настоящее время я использую Retrofit 2.3 в своем проекте Android, и недавно используемый нами API был обновлен, так что он должен иметь "version":number в теле JSON во всех запросах POST. Допустим, нам нужно передать объект UserCredentials - ранее тело запроса было просто сериализовано с использованием преобразователя GSON и выглядело так:

{"username":"myUsername", "password":"myPassword"}

и теперь у него должно быть дополнительное поле "версия":

{"username":"myUsername", "password":"myPassword", "version":1}

Я пару часов гуглил, как настроить фабрику пользовательских преобразователей на модернизацию, но все, что я нашел, это как исключить определенные поля из сериализации. Я знаю, что мог бы просто добавить поле "version" ко всем моим POJO, но я нашел этот подход «грязным», так как он будет использоваться только при отправке данных на сервер.

Кто-нибудь делал что-то подобное ранее?

Ответы [ 2 ]

0 голосов
/ 07 ноября 2018

Решена эта проблема с настраиваемыми перехватчиками для клиента OkHTTP. Поэтому я создал собственный перехватчик, который будет анализировать исходящий запрос и изменять его данные JSON, если он соответствует определенным критериям.

okHttpClientBuilder.addInterceptor(CustomInterceptor(1))

Работает как шарм!

import okhttp3.Interceptor
import okhttp3.RequestBody
import okhttp3.Response
import okio.Buffer
import org.json.JSONException
import org.json.JSONObject
import java.io.IOException

class CustomInterceptor (private val version: Int): Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        val requestBody = request.body()
        val subtype = requestBody?.contentType()?.subtype()
        if(subtype != null
                && subtype.contains("json")) {
            val bodyWithToken = addVersionParamToJSONBody(requestBody)
            if(null != bodyWithToken){
                val requestBuilder = request.newBuilder()
                request = requestBuilder
                        .post(bodyWithToken)
                        .build()
            }
        }
        return chain.proceed(request)
    }

    private fun addVersionParamToJSONBody(requestBody: RequestBody?) : RequestBody? {
        val customRequest = bodyToString(requestBody)
        return try{
            val jsonObject = JSONObject(customRequest)
            jsonObject.put("version", version)
            RequestBody.create(requestBody?.contentType(), jsonObject.toString())
        } catch (e: JSONException){
            e.printStackTrace()
            null
        }
    }

    private fun bodyToString(requestBody: RequestBody?): String{
        return try {
            val buffer = Buffer()
            requestBody?.writeTo(buffer)
            buffer.readUtf8()
        } catch (e: IOException){
            e.printStackTrace()
            ""
        }
    }
}    
0 голосов
/ 07 ноября 2018

Я сделал это, но не совсем так, как вы хотите, вы можете создать класс POJO BaseRequest, в котором вы можете использовать номер версии и расширить этот класс на другие классы POJO, которые вы используете следующим образом:

class BaseRequest{
@SerializedName("version")
public int version= 0;
 }

Расширить этот базовый класс POJO на другие классы POJO, чтобы использовать номер версии следующим образом:

class UserRequest extends BaseRequest{
  @SerializedName("username")
  public String userName = "";
  @SerializedName("password")
  public String password = "";
}

Есть много преимуществ этого подхода, например, если вам нужно еще одно поле в ваших API, вам не нужно менять все API-интерфейсы. Вы можете достичь этого, просто добавив поле в свой базовый запрос.

...