I have a problem with handling responses in YAML format in Retrofit. Until now I only handled the response body in JSON format or plain text. For these types, I always have prepared converters like gson, jackson for JSON and scalars for plain text. I found repository with all converters: retrofit-convertes.
In the documentation is a short mention that I need to create my own converter for this type.
If you need to communicate with an API that uses a content-format that Retrofit does not support out of the box (e.g. YAML, txt, custom format) or you wish to use a different library to implement an existing format, you can easily create your own converter. Create a class that extends the Converter.Factory class and pass in an instance when building your adapter.
Sadly, I don't see any tutorial on how to create such a converter. Is there any documentation explaining how to do this or is there any other option to handle such a case?
In your case you can use Jackon with yaml data formats.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("<your base url>")
.addConverterFactory(JacksonConverterFactory.create(new ObjectMapper(new YAMLFactory())))
.build();
For more informating check the following links:
https://github.com/square/retrofit/tree/master/retrofit-converters/jackson
https://github.com/FasterXML/jackson-dataformats-text/tree/master/yaml
Related
I came across one issue with some of the response we getting is not straight to parse and convert it to POJO. The format of response I am getting is as below
[
"list",
[
{
"#type": "com.exampe.model.ModelName",
"number": 1,
"name": "Test Name",
"url": "/test/url/",
"type": "f"
}
]
]
I want to ignore that "list" and parse a POJO in List of object ModelName. I am using Retrofit and Moshi Convertor but I am not sure how I can achieve this. Is there any way that I can intercept the response before it passed to Moshi Convertor or any different approach that I can go for.
Retrofit Snippet
private fun getRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl(BuildConfig.API_URL)
.addConverterFactory(MoshiConverterFactory.create())
.client(getHTTPClient())
.build()
}
Retrofit offers custom converters (see official documentation),moshi also should offer something similar.
I do not have experience in using moshi, but I have checked documentation and source code - it looks it is possible.
Moshi offers custom adapters which should do things you need. Take a look on PolymorphicJsonAdapterFactory, it has methods fromJson() and toJson() which allows you manually parse json elements in which way you like.
Even more. PolymorphicJsonAdapterFactory looks as an option you need for this.
A JsonAdapter factory for objects that include type information in
the JSON. When decoding JSON. Moshi uses this type information to
determine which class to decode to. When encoding Moshi uses. the
object’s class to determine what type information to include.
I am trying to parse the results of an API call which returns a unique first property.
{
"AlwaysDifferent12345": {
"fixedname1" : "ABC1",
"fixedname2" : "ABC2"
}
}
I am using retrofit2 and jackson/gson and cannot figure out how to cope with dynamic property names within the retrofit2 framework. The following works fine
data class AlwaysDifferentDTO(
#JsonProperty("AlwaysDifferent12345") val alwaysDifferentEntry: AlwaysDifferentEntry
)
I have tried
data class AlwaysDifferentDTO(
#JsonProperty
val response: Map<String, AlwaysDifferentEntry>
)
But this returns errors Can not instantiate value of type... The return value from the API is fixed i.e. map<string, object>.
I have read you can write a deserializer but it looks like I need to deserialize the whole object when all I want to do is just ignore the string associated with the response.
I have read
https://discuss.kotlinlang.org/t/set-dynamic-serializedname-annotation-for-gson-data-class/14758
and several other answers. Given unique properties names are quite common it would be nice to understand how people deal with this when using retrofit2
Thanks
Because the JSON doesn't have a 1-to-1 mapping Jackson can't map it automatically using annotations. You are going to need to make your own Deserializer.
In this tutorial you can learn how to create your own custom Deserializer for Jackson. https://www.baeldung.com/jackson-deserialization
In the tutorial you will see the first line under the deserialize function is
JsonNode node = jp.getCodec().readTree(jp);
using this line you can get the JSON node as a whole and once you have it you can call this function
JsonNode AlwaysDifferent12345Node = node.findParent("fixedname1");
Now that you have that node you can retrieve its value like shown in the rest of the tutorial. Once you have all the values you can return a new instance of the AlwaysDifferentDTO data class.
I see online some people use ScalerConverterFactory.create() to deserialize Strings and other primitive types. Is this necessary or can Moshi and kotlinx serialization handle these primitive types?
It depends on whether you're using JSON. If your response body is unquoted, use the Scalar converter:
Hello I'm plain text
If it's quoted, use a JSON converter.
"Hello, I'm a JSON string"
How do I serialize a Json Array like the the picture I am going to upload? Sorry If this question may be repetitive but I cannot figure out how to do it in my use case.
For more context here are some photos.
https://imgur.com/a/NvbbnMI
I see you're using Retrofit, so it has to do it automatically. Just change return type to:
Call<List<petProfile>>
If your Retrofit is not doing it, add ->
.addConverterFactory(GsonConverterFactory.create())
after Retrofit.Builder()
there are a number of libs handling serialize and deserialize json.
such as Gson and JsonSlurpper.
Here you can see the Gson example:
new Gson().toJson(/*place your POJO here*/); // serialize
new Gson().fromJson(/*json object*/, /*class to convert to*/); //deserialize
I'd like to send a POST request with a content like the following:
api=1&os=android&appVersion=12345
Is it possible to have a POJO with just that variables and pass that to
#FormUrlEncoded
#POST("/sendData")
void sendData(#FieldMap MyPojo myPojo, Callback<MyResponse> callback);
With FieldMap it doesn't work, is there another way?
Unfortunately this won't work out of the box. You have two options:
If you only have a handful of Pojos, you can define a MyPojo.toFieldMap() method to build a field map of form values and pass it to your sendData() method.
If you have a large number of Pojos, you can write a custom FormEncodedConverter by implementing the Converter interface in your project. I reckon this will need a combination of annotations and reflection to discover the public members of your MyPojo class. Examples of custom converters are available here.