Retrofit - add field dynamically during serialization











up vote
0
down vote

favorite












I'm currently using Retrofit 2.3 in my Android project and recently the API we are using was updated so that it needs to have "version":number in JSON body in all POST requests. So let's say we need to pass UserCredentials object - previously body of the request was simply serialized using GSON converter and looked like this



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



and now it has to have additional "version" field:



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



I've googled couple of hours how to set custom converter factory to retrofit but all I found was how to exclude certain fields from serialization. I know I could simply add "version" field to all my POJOs but I found this approach 'dirty' as it's going to be used only during sending data to server.



Has anyone did something like this previously?










share|improve this question






















  • Can you expose your Retrofit API method declaration just to clarify a bit how you send the login data?
    – pirho
    Nov 8 at 9:41










  • Based on your answer you are using Kotlin or something. Maybe you update that tag in to your question. I created Java based Q&A here in case you want to try to apply that solution also.
    – pirho
    Nov 8 at 14:17















up vote
0
down vote

favorite












I'm currently using Retrofit 2.3 in my Android project and recently the API we are using was updated so that it needs to have "version":number in JSON body in all POST requests. So let's say we need to pass UserCredentials object - previously body of the request was simply serialized using GSON converter and looked like this



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



and now it has to have additional "version" field:



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



I've googled couple of hours how to set custom converter factory to retrofit but all I found was how to exclude certain fields from serialization. I know I could simply add "version" field to all my POJOs but I found this approach 'dirty' as it's going to be used only during sending data to server.



Has anyone did something like this previously?










share|improve this question






















  • Can you expose your Retrofit API method declaration just to clarify a bit how you send the login data?
    – pirho
    Nov 8 at 9:41










  • Based on your answer you are using Kotlin or something. Maybe you update that tag in to your question. I created Java based Q&A here in case you want to try to apply that solution also.
    – pirho
    Nov 8 at 14:17













up vote
0
down vote

favorite









up vote
0
down vote

favorite











I'm currently using Retrofit 2.3 in my Android project and recently the API we are using was updated so that it needs to have "version":number in JSON body in all POST requests. So let's say we need to pass UserCredentials object - previously body of the request was simply serialized using GSON converter and looked like this



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



and now it has to have additional "version" field:



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



I've googled couple of hours how to set custom converter factory to retrofit but all I found was how to exclude certain fields from serialization. I know I could simply add "version" field to all my POJOs but I found this approach 'dirty' as it's going to be used only during sending data to server.



Has anyone did something like this previously?










share|improve this question













I'm currently using Retrofit 2.3 in my Android project and recently the API we are using was updated so that it needs to have "version":number in JSON body in all POST requests. So let's say we need to pass UserCredentials object - previously body of the request was simply serialized using GSON converter and looked like this



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



and now it has to have additional "version" field:



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



I've googled couple of hours how to set custom converter factory to retrofit but all I found was how to exclude certain fields from serialization. I know I could simply add "version" field to all my POJOs but I found this approach 'dirty' as it's going to be used only during sending data to server.



Has anyone did something like this previously?







android gson retrofit






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 7 at 17:28









Alexandr Zhurkov

757




757












  • Can you expose your Retrofit API method declaration just to clarify a bit how you send the login data?
    – pirho
    Nov 8 at 9:41










  • Based on your answer you are using Kotlin or something. Maybe you update that tag in to your question. I created Java based Q&A here in case you want to try to apply that solution also.
    – pirho
    Nov 8 at 14:17


















  • Can you expose your Retrofit API method declaration just to clarify a bit how you send the login data?
    – pirho
    Nov 8 at 9:41










  • Based on your answer you are using Kotlin or something. Maybe you update that tag in to your question. I created Java based Q&A here in case you want to try to apply that solution also.
    – pirho
    Nov 8 at 14:17
















Can you expose your Retrofit API method declaration just to clarify a bit how you send the login data?
– pirho
Nov 8 at 9:41




Can you expose your Retrofit API method declaration just to clarify a bit how you send the login data?
– pirho
Nov 8 at 9:41












Based on your answer you are using Kotlin or something. Maybe you update that tag in to your question. I created Java based Q&A here in case you want to try to apply that solution also.
– pirho
Nov 8 at 14:17




Based on your answer you are using Kotlin or something. Maybe you update that tag in to your question. I created Java based Q&A here in case you want to try to apply that solution also.
– pirho
Nov 8 at 14:17












2 Answers
2






active

oldest

votes

















up vote
1
down vote













I did it but not exactly the way you want, you can create BaseRequest POJO class in which you can use version number and extend that class to other POJO classes you are using like this :



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


Extend this base POJO class is to other POJO classes to use the version number like this :



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


There are lots of benefits of this approach like if you need one more field in your APIs then you don't need to change all of the apis. You can achieve that just by adding a field in your baserequest.






share|improve this answer





















  • I wouldn't want to do that due to several factors: "version" is used only during API calls and that I would prefer composition over inheritance
    – Alexandr Zhurkov
    Nov 7 at 19:19


















up vote
0
down vote













Resolved this issue with custom interceptors for OkHTTP client. So I've created custom interceptor that would analyze outgoing request and alter its body JSON data if it meets certain criteria.



okHttpClientBuilder.addInterceptor(CustomInterceptor(1))


Works like a charm!



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()
""
}
}
}





share|improve this answer





















    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














     

    draft saved


    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53194710%2fretrofit-add-field-dynamically-during-serialization%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    1
    down vote













    I did it but not exactly the way you want, you can create BaseRequest POJO class in which you can use version number and extend that class to other POJO classes you are using like this :



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


    Extend this base POJO class is to other POJO classes to use the version number like this :



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


    There are lots of benefits of this approach like if you need one more field in your APIs then you don't need to change all of the apis. You can achieve that just by adding a field in your baserequest.






    share|improve this answer





















    • I wouldn't want to do that due to several factors: "version" is used only during API calls and that I would prefer composition over inheritance
      – Alexandr Zhurkov
      Nov 7 at 19:19















    up vote
    1
    down vote













    I did it but not exactly the way you want, you can create BaseRequest POJO class in which you can use version number and extend that class to other POJO classes you are using like this :



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


    Extend this base POJO class is to other POJO classes to use the version number like this :



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


    There are lots of benefits of this approach like if you need one more field in your APIs then you don't need to change all of the apis. You can achieve that just by adding a field in your baserequest.






    share|improve this answer





















    • I wouldn't want to do that due to several factors: "version" is used only during API calls and that I would prefer composition over inheritance
      – Alexandr Zhurkov
      Nov 7 at 19:19













    up vote
    1
    down vote










    up vote
    1
    down vote









    I did it but not exactly the way you want, you can create BaseRequest POJO class in which you can use version number and extend that class to other POJO classes you are using like this :



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


    Extend this base POJO class is to other POJO classes to use the version number like this :



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


    There are lots of benefits of this approach like if you need one more field in your APIs then you don't need to change all of the apis. You can achieve that just by adding a field in your baserequest.






    share|improve this answer












    I did it but not exactly the way you want, you can create BaseRequest POJO class in which you can use version number and extend that class to other POJO classes you are using like this :



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


    Extend this base POJO class is to other POJO classes to use the version number like this :



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


    There are lots of benefits of this approach like if you need one more field in your APIs then you don't need to change all of the apis. You can achieve that just by adding a field in your baserequest.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Nov 7 at 18:05









    Ajay Chauhan

    152114




    152114












    • I wouldn't want to do that due to several factors: "version" is used only during API calls and that I would prefer composition over inheritance
      – Alexandr Zhurkov
      Nov 7 at 19:19


















    • I wouldn't want to do that due to several factors: "version" is used only during API calls and that I would prefer composition over inheritance
      – Alexandr Zhurkov
      Nov 7 at 19:19
















    I wouldn't want to do that due to several factors: "version" is used only during API calls and that I would prefer composition over inheritance
    – Alexandr Zhurkov
    Nov 7 at 19:19




    I wouldn't want to do that due to several factors: "version" is used only during API calls and that I would prefer composition over inheritance
    – Alexandr Zhurkov
    Nov 7 at 19:19












    up vote
    0
    down vote













    Resolved this issue with custom interceptors for OkHTTP client. So I've created custom interceptor that would analyze outgoing request and alter its body JSON data if it meets certain criteria.



    okHttpClientBuilder.addInterceptor(CustomInterceptor(1))


    Works like a charm!



    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()
    ""
    }
    }
    }





    share|improve this answer

























      up vote
      0
      down vote













      Resolved this issue with custom interceptors for OkHTTP client. So I've created custom interceptor that would analyze outgoing request and alter its body JSON data if it meets certain criteria.



      okHttpClientBuilder.addInterceptor(CustomInterceptor(1))


      Works like a charm!



      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()
      ""
      }
      }
      }





      share|improve this answer























        up vote
        0
        down vote










        up vote
        0
        down vote









        Resolved this issue with custom interceptors for OkHTTP client. So I've created custom interceptor that would analyze outgoing request and alter its body JSON data if it meets certain criteria.



        okHttpClientBuilder.addInterceptor(CustomInterceptor(1))


        Works like a charm!



        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()
        ""
        }
        }
        }





        share|improve this answer












        Resolved this issue with custom interceptors for OkHTTP client. So I've created custom interceptor that would analyze outgoing request and alter its body JSON data if it meets certain criteria.



        okHttpClientBuilder.addInterceptor(CustomInterceptor(1))


        Works like a charm!



        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()
        ""
        }
        }
        }






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 7 at 19:18









        Alexandr Zhurkov

        757




        757






























             

            draft saved


            draft discarded



















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53194710%2fretrofit-add-field-dynamically-during-serialization%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            這個網誌中的熱門文章

            Tangent Lines Diagram Along Smooth Curve

            Yusuf al-Mu'taman ibn Hud

            Zucchini