Kotlin Retrofit: Need to convert Empty Array to Null - kotlin

I'm new to Retrofit2. I have a legacy backend that's swapping a data object values with an empty array when the value is empty.
Scenario #1 Normal data response:
{data:{"name","john"}}
#Scenario 2 Alternate data response:
{data:[]}
Retrofit obviously can't map both data types without a converter (that I know of) automatically. When the data returns the empty array, the parser errors as expected.
I've attempted to use EmptyToNull converters, which fixes the error, but it also nulls out the normal data object returned in scenario #1.
My data classes are as follows:
data class ResponseData(
error: String,
message: String,
#SerializedName("data")
details: Details?
)
data class Details(
id: Int,
info: String
)
I've searched all over StackOverflow and other tutorials online, but I can't figure out how to detect the data / details value to ensure it's converted to null.
Thank you in advance!

Related

Jooq: JsonConverter not converting jsonb into list of class when fetching data

This is a continuation of a first question I asked here: Jooq: How can I map a JSONB column to a Kotlin data class field?
Although I'm able to create new records just fine with the changes mentioned there, I'm not being able to fetch data like so:
fun findAllTrackedEvents(): List<TrackedEvent> {
return dslContext.select(*TRACKED_EVENT.fields())
.from(TRACKED_EVENT)
.fetchInto(TrackedEvent::class.java)
}
It seems that jackson is mapping the rows into LinkedHashMaps instead of mapping them into the fields of the Metadata data class. This is the error I'm getting:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON:
object is not an instance of declaring class; nested exception is com.fasterxml.jackson.databind.JsonMappingException:
object is not an instance of declaring class (through reference chain: java.util.ArrayList[0]->com.my.project.tracked_event.TrackedEvent["metadata"]->java.util.ArrayList[0]->java.util.LinkedHashMap["tableRef"])]
data class TrackedEvent(
val id: UUID,
// other fields
val metadata: List<Metadata> // this metadata field in the database is of type jsonb
)
data class Metadata(
val tableRef: String,
val value: UUID
)
So it can convert the field properly when inserting but not when fetching?
In my previous answer, I suggested you use arrays instead of lists. This had a reason. Consider this:
fun main() {
val a: Array<Int?> = arrayOf(1)
println(a::class.java)
val b: List<Int?> = listOf(1)
println(b::class.java)
}
It prints:
class [Ljava.lang.Integer;
class java.util.Collections$SingletonList
As you can see, while arrays are reified on the JVM, other generic types are not, and the T type variable of List<T> is erased. It is possible that Jackson cannot figure out the correct type to unmarshal at runtime using reflection, despite all the type information being available at compile time.
I would just use Array<Metadata> instead. Alternatively, of course, you can attach a custom converter to the column, instead of using the out of the box <jsonConverter>. That way, you're in full control of the mapping.

How to replace null values to in map during deserialization in kotlin

I have data class, which is used for place response from some REST service:
data class RestResponse(
val themes: List<Map<String, String?>> = emptyList(),
)
and some values inside map could be empty strings.
But there is configuration in project that affect this:
enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)
so, all empty strings are casted to null values.
I don't want to fix this setting for the whole project, but only want to empty strings for map in this response don't be cast to null values.
What is the best way to solve this?
As i see there could be different solutions:
Write custom deserializer
Fix null values after creating an response object
But it would be better for me if i could customize setting
enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)
for only this data class.Is it possible?
P.S. - i use Jackson lib for mapping

How to work with schema returned by 'get_catalog_schema_as_spark_schema'?

Example:
schema = glueContext.get_catalog_schema_as_spark_schema(database=args['Database'], table_name=args['Table'])
if I simply print the returned schema I can see the StructType/StructField structure, something similar to:
StructType(
StructField(column1,StringType,true),
StructField(column2,StringType,true)
)
The object itself is java object and it does not seem to match StructType described in https://github.com/awslabs/aws-glue-libs/blob/master/awsglue/gluetypes.py
if I try to iterate through fields property, it throws error that fields is not iterable.
How do I work with this object? Ideally I want to either be able to convert it into JSON, or atleast get the list of columns.
I appreciate any help here.

JSON Deserialization of collections that contain null values

I get the following JSON string:
{"userIds":[null, "FA9C67A8-1C22-4393-A701-136C85BB0D6F"]}
I would like to deserialize this to a set with one element (the UUID). My target class looks somewhat like this:
data class TestDto(
val userIds: Set<UUID> = emptySet(),
[...]
)
I am using Kotlin 1.5.21, Spring-Boot 2.4.4. The type of userIds does not allow null values in the set. However, when deserializing the JSON string the set consists of two values, the null value and the UUID. This leads to a NullPointerException later which I actually thought would be eliminated by setting the type to Set<UUID> instead of Set<UUID?>.
How can I sort of ignore/filter out any null values on deserialization if provided?
I think
#JsonSetter(contentNulls = Nulls.SKIP)
val userIds: Set<UUID> = emptySet(),
[...]
Should do the trick

JSON Deserialize Error

I wanna pass the json to server, below is the json format:
[{"StaffID":"S01","StaffRank":"Manager"},{"StaffID":"S02","StaffRank":"Waiter"}]
After I tried the following code to get the json array:
Dim request As String = New StreamReader(data).ReadToEnd
response = AddStaff(JsonConvert.DeserializeObject(Of tbl_Staff)(request))
Return JsonConvert.SerializeObject(response)
I get the new error which is:
"Cannot deserialize the current JSON array (e.g. [1,2,3]) into type
'tbl_Staff' because the type requires a JSON object
(e.g.{"name":"value"}) to deserialize correctly. To fix this error
either change the JSON to a JSON object (e.g. {"name":"value"}) or
change the deserialized type to an array or a type that implements a
collection interface (e.g. ICollection, IList) like List that can
be deserialized from a JSON array. JsonArrayAttribute can also be
added to the type to force it to deserialize from a JSON array."
What is the problem? Thanks
The problem is you cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'tbl_Staff' because the type requires a JSON object (e.g.{"name":"value"}) to deserialize correctly. To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
It seems like no matter how detailed I make that error message, people just don't read it :-\
I found the answer.I just change the code to List. Then working perfectly.
Dim request As String = New StreamReader(data).ReadToEnd
response = AddStaff(JsonConvert.DeserializeObject(Of List(Of tbl_Staff))(request))
Return JsonConvert.SerializeObject(response)