How to replace null values to in map during deserialization in kotlin - 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

Related

Kotlin Retrofit: Need to convert Empty Array to Null

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!

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

Confused about the following line #field:[Expose SerializedName("id")]

I have the following data class that will retrieve data from an API:
data class Users(
#field:[Expose SerializedName("id")]
val id: Int)
I am just wondering what the #field: means.
Normally, I have always done like this:
data class Users(
#Expose
#SerializedName("id")
val id: Int)
I understand the meaning of expose and serializedName.
Just a few questions:
My best guess would be for the #field:[] would be to take an array of annotations, instead of putting them on each line as in the second example?
But is the field a Kotlin keyword or an annotation as it's preceded by the #?
Where else could you use the #field?
The val id in your example is declaring several different things in one go:
A constructor parameter.
A property of the class, implemented as a getter method.
A backing field for the property.
So which of those does an annotation get applied to?  It defaults to the parameter, and that's what your second example does.
If you want it to apply to the field instead, as in your first example, you use the field: target.
(It usually applies to single annotations, but it can apply to an array of them, as in this case.)
For more details, see the link jonrsharpe provided: https://kotlinlang.org/docs/reference/annotations.html#annotation-use-site-targets
The field:, property:, file:, &c targets are only for use with annotations.  (field is also a keyword within getter/setter definitions.)

Jackson's SORT_PROPERTIES_ALPHABETICALLY for Map

I use Jackson 2.5.0.
I would like to write a method which takes arbitrary JSON-string and sorts every property by key alphabetically with Jackson. Including nested ones.
I learned there is a SORT_PROPERTIES_ALPHABETICALLY feature of Jackson's ObjectMapper which I wanted to use in order to achieve my goal. So my initial code based on this idea is:
class FooBar {
String foo
String bar
}
def sortFields(String source) {
def om = new ObjectMapper().configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
def obj = om.readValue(source, Map.class)
return om.writeValueAsString(obj)
}
println sortFields('{"foo":"f","bar":"b"}')
Notice that I can't know ahead what structure input JSON has so Jackson unmarshalls it as a Map by default (LinkedHashMap to be more precise).
I expected it to output a string with keys sorted alphabetically:
{"bar":"b","foo":"f"}
Unfortunately with the snippet above SORT_PROPERTIES_ALPHABETICALLY does not work when object to serialize is a Map. If I replace Map.class with FooBar.class my JSON properties will be sorted as expected. But as I said, I can't know ahead a type of input JSON and have a class in my code for any type possible. What other options do I have with Jackson?
This works for the top level map:
objectMapper.configure(
SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true
);
Sadly it doesn't sort keys in any potential nested map you may have.

Accessing properties of a Kotlin entity

I'm new to Kotlin, so apologies if I'm not articulating concepts correctly. I have an instance of an Entity
[TestEntity(applicationId=1, timestamp=2018-01-24T18:40:30, issueState=MA, product=test, status=sold, paymentMode=VISA, premium=null)]
I am writing a service that is going to take these keys and use them to create the headers of a report. The keys may change depending on the type of report the user is trying to generate, which will have an impact on the Entity that will be instantiated.
I want to be able to iterate over this Entity so that I can create an array to use for the headers. Any thoughts on how I do this?
I think the cleanest solution is storing values in a map and delegating properties to it.
Don't think you can otherwise iterate over class fields without some verbose getter chain or ugly reflection shenanigans.
For example here you can access map fields as if they were class fields, but can also easily iterate over map.
data class TestEntity(val map : Map<String, Any>){
val appId : Int by map
val timeStamp : Long by map
(... more fields)
}