kotlinx.serialization Decode object from map value taking ID from the map key - kotlin

Using koltinx.serialization, is there a way to enable the "value" serializer to take it's ID from the "key" of the map entry?
For example, given the following JSON:
{
"data": {
"key1": { "name": "value1" },
"key2": { "name": "value2" },
}
}
And the serializable data class:
#Serializable
data class DataEntry(val id: String, val name: String)
How can I write a KSerializer for the map (or for the map "value") that will enable the DataEntry serializer to take the value of it's id field from the map "key"?
The result being a List (or Map) of:
DataEntry("key1", "value1"),
DataEntry("key2", "value2"),
I know that I can deserialise the map using DataEntry(val name: String) (without id) and then build a list of entries with id's from that. But in my case, for the map "value" I actually have many large data types, and this would require duplicating a lot of code. I want a generic way to handle this scenario given any data type.

Related

Create generic transform serializer

I have a beautiful JSON, where numbers (but not only) are "nulled" in the flowing way:
[{
"bar": ""
},
{
"bar": 123
}]
I would like to parse it to the:
data class(val bar: Long?)
I found out it can be quite easily done with following transformer.
object NullableStringSerializer : JsonTransformingSerializer<Long>(serializer()) {
override fun transformDeserialize(element: JsonElement): JsonElement =
if (element is JsonPrimitive && element.isString && element.content.isEmpty())
JsonNull
else element
}
#Serializable
data class(#Serializable(with = NullableStringSerializer::class) val bar: Long?)
This works nice, however I would like to make it more generic, so I wont need to write this transformer for every possible type.
Sadly due to the "generics" works in Kotlin, adding the type parameter to the object is not possible, and after changing it to be a class, serializer<T>() is crying about not having a refined type.
How can I make my NullableStringSerializer generic?
I just couldn't get NullableStringSerializer to work with empty strings at all (are you sure yours actually works?) In the end, I got it working like this:
#Serializable
data class X(val bar: JsonPrimitive) {
fun barValue() = bar.longOrNull
}

How to disable kotlinx serialization polymorphic descriminator?

I'm generating JSON for a number of third-party APIs. Many of them accept a list (JSON array) of different objects, however, none of them will accept the "type": "com.mycom.someclass" automatically generated by kotlinx serialization due to the polymorphic nature of the list.
In my use-case, I only care about serializing out. No deserialization is needed.
Current:
[{"type":"com.mycom.Child1","x":"child1"}, {"type":"com.mycom.Child2","y": 23}]
Needed:
[{"x":"child1"}, {"y": 23}]
How can I disable this automatic behavior?
Take a look at Json parametric polymorphic deserialization:
You also can serialize data with such serializer. In that case, either registered or default serializer would be selected for the actual property type in runtime. No class discriminator would be added.
You'll need to impelement JsonParametricSerializer and manually select the serializer. As you don't need to support deserialization, the implementation would be trivial.
The trick is to make the polymorphic type distinctive. As an example
sealed class Vehicle {
data class Car(...)
data class Bike(...)
}
val v: Vehicle = Vehicle.Car()
// Contains "type" as you serialize Vehicle, which is polymorphic
val json1 = Json.encodeToString(v)
// Does not contain "type" as you serialize Vehicle.Car, which is distinctive
val json2 = when(v) {
is Vehicle.Car -> Json.encodeToString(v)
is Vehicle.Bike -> Json.encodeToString(v)
}
Same when you'd use this in Ktor:
// Contains "type" as you serialize Vehicle, which is polymorphic
setBody(v)
// Does not contain "type" as you serialize Vehicle.Car, which is distinctive
val json2 = when(v) {
is Vehicle.Car -> setBody(v)
is Vehicle.Bike -> setBody(v)
}

Using JSON Schema, how do I validate an object where keys and values are not known in advance?

i need to validate this format:
{
"variables": {
"team": "red",
"L3_HCU_TESTS": "N",
"dbo_user": "user",
"version": 920
}
}
I don't know in advance names of keys and values.
"variables" are not mandatory but if it's provided,
keys can be any string, and value can be any value.
What is important for me is to validate that "variables" (if provider) will include key and value.
Empty values are not allowed
With a JSON Object, the keys MUST be a string, so your requirement for string based keys is already covered by "is valid JSON".
Expanding your requirements, you want:
"variables" must be an object
There must be at least one property in the object
The value of the properties must not be null
Here's a schema which satisfies those requirements:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"variables": {
"type": "object",
"minProperties": 1,
"additionalProperties": {
"not": {
"type": "null"
}
}
}
}
}
You can test it against your instance using this link.
"type": "object" makes sure that "variables" is an object.
"minProperties" makes sure there is a minimum number of properties in an object.
"additionalProperties" value subschema is applied to all key values in the object (as "properties" and "patternProperties" are not defined). The subschema inverts the result of a subschema, which defines the type must be null. The inversion results in, must be NOT NULL.
I haven't seen minProperties used very often. Here's the reference link for that JSON Schema keyword: https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-6.5.2

How to use the class data of a json element that have colon in name - Kotlin

I have the following Json:
_embedded: {
wp:featuredmedia: [
{
id: 9060,
date: "2018-05-28T17:41:21",
author: 2,
caption: {
rendered: ""
},
source_url: "h.t.t.p.s://mydomain_com/myimage.jpg",
}
]
}
I am using httpOk and Gson to deserialize, I have the following data classes.
data class Embedded(
val wp:featuredmedia: List<Wpfeaturedmedia>
)
data class Wpfeaturedmedia(
val source_url: String
)
Everything works correctly, but my problem is when it comes to obtaining the image of source_url, because the name wp:featuredmedia has two points (colon).
How do I make the class work correctly?
UPDATE: WORK NOW WITH
data class Embedded(
#SerializedName("wp:featuredmedia") val wpfeaturedmedia: List<Wpfeaturedmedia>
)
It's simple - name your field wpfeaturedmedia (without the colon) and it will be mapped correctly.
Edit:
It seems I was wrong - Gson simply skips wp:featuredmedia during deserialization unless field wpfeaturedmedia is annotated with #SerializedName("wp:featuredmedia")
you may use #JsonProperty
data class ResponseData(
#JsonProperty("wp:featuredmedia")
val wpfeaturedmedia: List<SubData>
) { data class SubData(...)}

Ignoring null fields when deserializing with Jackson

I have the following Kotlin data class:
data class TestObject(
val boolField: Boolean,
val stringField: String,
val nullBoolField: Boolean = true,
val nullStringField: String = "default",
val notThereBoolField: Boolean = true,
val notThereStringField: String = "not there"
)
I am then attempting to deserialize some JSON into this class using Jackson v2.9.4 with the Jackson Kotlin plugin v2.9.4.1. The test JSON is as follows:
{
"boolField": true,
"stringField": "string",
"nullBoolField": null,
"nullStringField": null
}
The first two and the last two fields deserialize successfully - with the values from the JSON and the default values from the Kotlin data class, as appropriate. However, when the middle pair of fields are present, the deserialization fails with:
Instantiation of [simple type, class com.example.TestObject] value
failed for JSON property nullStringField due to missing (therefore
NULL) value for creator parameter nullStringField which is a
non-nullable type
I know I could solve the problem by changing the types of nullBoolField and nullStringField to Boolean? and String? respectively, but since default values are known I would rather not have to push the handling of null further down into the code by doing that.
The question is: is there any way of configuring Jackson to use the Kotlin data class default values for non-nullable properties when the property is present in the JSON but set to null?
You could try first to filter null values from json and after to deserialize it to Kotlin object.
Or you may to try add feature to kotlin-jackson module, with adding a new feature parameter, which will enable a null ignoring from json parameters and use default values.
You may do this by modify this line (if I'm not mistaken)
If you don't have any value and field is non-nullable then you should not pass it in request body:
{
"boolField": true,
"stringField": "string"
}
This results in following object, as expected:
{
"boolField": true,
"stringField": "string",
"nullBoolField": true,
"nullStringField": "default",
"notThereBoolField": true,
"notThereStringField": "not there"
}