Jackson parses a map of strings to a declared class as a map of strings to maps. How do I make it create objects of the declared class instead? - kotlin

I'm using jackson-module-kotlin:2.11.2 to parse some YAML. I'm trying to produce objects which contain a map, whose values are objects of a class that I have declared. This map instead contains values which are HashMaps.
Here are my declarations:
import com.fasterxml.jackson.module.kotlin.readValue
object Parser {
// ObjectMapper is thread safe as long as we don't mess with the config after this declaration.
val mapper: ObjectMapper = ObjectMapper(YAMLFactory()).registerKotlinModule()
.registerModule(JavaTimeModule())
.registerModule(nullMapDeserialiserModule)
.registerModule(SimpleModule().setDeserializerModifier(ValidatingDeserializerModifier()))
// When parsing timestamps, we don't want to lose the offset information
.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
// We would prefer an error if we're trying to store a float in an int
.disable(DeserializationFeature.ACCEPT_FLOAT_AS_INT)
// If a primitive field (like Int) is non-nullable (as in the Kotlin meaning), then we don't want nulls being converted to 0
.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
// Because enums could change order in future versions (if we keep them in lexicographic order, for example),
// we don't want the client to expect that they can give us the ordinal value of the enum.
.enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)
// When serialising schedule expressions, don't include null values
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
#Throws(JsonProcessingException::class)
inline fun <reified T: Any> deserialise(yaml: String): T = mapper.readValue(yaml)
}
data class ListValue (
val listValueKey: String,
val someOtherValue: Int
)
data class ExpectedValue (
val expectedValueKey: String,
val list: List<ListValue>
)
data class TestClass (
val testClassKey: String,
#param:JsonDeserialize(contentAs = ExpectedValue::class)
val testMap: Map<String, ExpectedValue>
)
Here is my test case:
#Test
fun `map parse test`() {
val testObj: TestClass? = RuleParser.deserialise(
//language=YAML
"""
testClassKey: the-key
testMap:
key1:
expectedValueKey: subKey1
list:
- listValueKey: someKey1
someOtherValue: 5
- listValueKey: anotherKey1
someOtherValue: 6
key2:
expectedValueKey: subKey2
list:
- listValueKey: someKey2
someOtherValue: 7
- listValueKey: anotherKey2
someOtherValue: 8
"""
)
assertTrue(testObj is TestClass)
assert(testObj.testMap is HashMap)
assertNotNull(testObj.testMap["key1"])
assert(testObj.testMap["key1"] is ExpectedValue)
assertEquals(
ExpectedValue(
expectedValueKey = "subKey1",
list = listOf(ListValue("someKey1", 5), ListValue("anotherKey1", 6))
),
testObj.testMap["key1"]
)
}
Currently, this test is failing on the final assertion, with the following error
Expected :ExpectedValue(expectedValueKey=subKey1, list=[ListValue(listValueKey=someKey1, someOtherValue=5), ListValue(listValueKey=anotherKey1, someOtherValue=6)])
Actual :{expectedValueKey=subKey1, list=[{listValueKey=someKey1, someOtherValue=5}, {listValueKey=anotherKey1, someOtherValue=6}]}
This is clearly not what I expected. If I instead parse a list of a declared class, this works correctly (example test follows).
#Test
fun `list parse test`() {
val testObj: ExpectedValue? = RuleParser.deserialise(
//language=YAML
"""
expectedValueKey: subKey1
list:
- listValueKey: someKey1
someOtherValue: 5
- listValueKey: anotherKey1
someOtherValue: 6
"""
)
assertTrue(testObj is ExpectedValue)
assertTrue(testObj.list[0] is ListValue)
assertEquals(
ListValue("someKey1", 5),
testObj.list[0]
)
}
So I'm a bit surprised that it is possible to parse a generic list in this way, but not a map. How do I get Jackson to create the map values that I expect, rather than HashMaps?

Your deserializer function is wrong. You must use the reified generic type in readValue method:
inline fun <reified T: Any> deserialise(yaml: String): T = mapper.readValue(yaml, T::class.java)

While it is odd that the annotation is needed at all (since it is not needed for lists of a declared type), it does work if the annotation is used as follows:
#field:JsonDeserialize(`as` = HashMap::class, contentAs = ExpectedValue::class)
This was not clear at first, because that javadoc for contentAs does not mention that as is also required.

Related

How do I initialize these arrays?

I want to have a class that stores three sets of 30 Foos each. I can declare them as arrays of Foo but I don't know how I can initialize them (give them default values) with 30 elements.
data class Container (
val first: Array<Foo>,
val second: Array<Foo>,
val third: Array<Foo>,
)
data class Foo (val a: Int, val b: Int)
There aren't too many ways to create an array in Kotlin and they are pretty straightforward. Depending on your needs, you can either use arrayOf() function, Array() constructor or create List first and then convert it into array.
Example with arrayOf():
val first = arrayOf(
Foo(0, 0),
Foo(1, 1),
...
)
Array():
val first = Array(30) { Foo(it, it) }
List:
val firstList = mutableListOf<Foo>()
firstList += Foo(0, 0)
firstList += Foo(1, 1)
...
first = firstList.toTypedArray()
If the code for generating these arrays is complicated, you can write it inside init {} block or create static function(s) that provides these arrays.

Transform Map Values to Data Class in Kotlin

I have a map whose values are an arraylist of different object types (LocalDate, LocalTime, Float, List(Float)).
I would like to transform the map values to a custom data class i have created.
I have tried destructuring the maps value components however i am getting error "Destructuring declaration initializer of type Map.Entry<String, Any> must have a 'component3()' function".
How would i perform this transformation below?
Blockquote
val localDate1 = LocalDate.now()
val localTime1 = LocalTime.now()
val float1 = 1f
val floatList1 = listOf<Float>(1f, 2f, 3f)
val localDate2 = LocalDate.now()
val localTime2 = LocalTime.now()
val float2 = 2f
val floatList2 = listOf<Float>(4f, 5f, 6f)
val myMap = HashMap<String, Any>()
myMap["keyOne"] = arrayListOf<Any>(localDate1, localTime1, float1, floatList1)
myMap["keyTwo"] = arrayListOf<Any>(localDate2, localTime2, float2, floatList2)
val newMap = myMap.mapValues { (date, time, float, floatList) -> /*Here i am getting destructuring error*/
CustomObject(
date,
time,
float,
floatList
)
}
Blockquote
data class CustomObject (
val date: LocalDate,
val time: LocalTime,
val float: Float,
val floatList: List<Float>
)
Map.mapValues() takes a lambda whose receiver is the map Entry, not its value.  (The method is named for the lambda's results, not its parameter.) So you can't destructure it that way.
There are also typecasting issues: myMap is a map whose values are Any, but you seem to be assuming that each value is actually a List of Any (with the actual elements being of the appropriate types in the appropriate order). So you need to do the necessary casting to tell the compiler what types they are (or at least, what types you assume they are…)
So you could destructure it as a separate step, e.g.:
val newMap = myMap.mapValues { entry ->
val (date, time, float, floatList) = entry.value as List<Any>
CustomObject(
date as LocalDate,
time as LocalTime,
float as Float,
floatList as List<Float>
)
}
Or you could just use the array values directly:
val newMap = (myMap as Map<String, List<Any>>).mapValues {
CustomObject(
it.value[0] as LocalDate,
it.value[1] as LocalTime,
it.value[2] as Float,
it.value[3] as List<Float>
)
}
However I think it would be a lot better to avoid having the lists in the first place. You're taking a lot of extra code to create the lists and then convert them — but much of that code is unsafe: each destructuring can fail with an exception (perhaps ArrayIndexOutOfBoundsException) if the list isn't long enough, and each cast can fail with a ClassCastException. Casts are always a code smell, especially unchecked ones like these; Kotlin's type system is powerful enough to express most things you're likely to want, so bypassing it like this tends to indicate that the design can be improved.
So if there's any way you can create your custom objects directly, that's likely to be shorter, safer, faster, and easier to read.
import java.time.LocalDate
import java.time.LocalTime
data class CustomObject(
val date: LocalDate,
val time: LocalTime,
val float: Float,
val floatList: List<Float>
)
val myMap = mapOf(
"keyOne" to listOf(LocalDate.now(), LocalTime.now(), 1f, listOf(1f, 2f, 3f)),
"keyTwo" to listOf(LocalDate.now(), LocalTime.now(), 2f, listOf(4f, 5f, 6f))
)
val newMap = myMap.mapValues {
CustomObject(
it.value[0] as LocalDate,
it.value[1] as LocalTime,
it.value[2] as Float,
(it.value[3] as List<*>).map { fl -> fl as Float }
)
}
println(newMap)

Is this a good solution for a flexible data class to compare two objects?

first, I'm a kotlin neebie ^^.
I want to compare to objects from a data class. But the objects have variables that can be changed.
Is the code example a good practice to solve this or is there a problem that i can't see?
Ty
data class Test1(val id : Int, var name: FlexibleProperty<String>)
class FlexibleProperty<T>(var value: T) {
override fun equals(other: Any?) = true
override fun hashCode() = 1
}
fun main() {
val test1 = Test1(1, FlexibleProperty("Hans"))
val test2 = test1.copy()
println("test1 == test2 ${test1 == test2}")
println("test1 === test2 ${test1 === test2}")
test2.name = FlexibleProperty("Dieter")
println("test1 == test2 ${test1 == test2}")
println("test1 === test2 ${test1 === test2}")
}
EDIT:// Sry, I was a little confused ^^. My detailed problem is: I want to add these objects into a set. If I use normal string variables, the objects are different, so the set has 2 objects. But if I add test1 and check set.contains(test2) with my FlexiableProperty, the result is true, so I have to update the object. I don't want to check the id outside of the objects (with maybe a map and the id as key)
Here the code snippet with a set:
data class Test1(val id : Int, val name: FlexibleProperty<String>)
data class FlexibleProperty<T>(var value: T) {
override fun equals(other: Any?) = true
override fun hashCode() = 1
}
fun main() {
val test1 = Test1(1, FlexibleProperty("Hans"))
val test2 = test1.copy(name = FlexibleProperty("Dieter"))
val setTest = mutableSetOf(test1)
if (setTest.contains(test2)) {
setTest.remove(test1)
}
setTest.add(test2)
println("set $setTest")
}
There's no specific problem with your solution per see, but it could be greatly improved.
First, name can still be a value, since you use copy() anyway:
data class Test1(val id : Int, val name: FlexibleProperty<String>)
val test2 = test1.copy(name = FlexibleProperty("Dieter"))
Having no mutable properties make your class thread safe, and easier to reason about.
Second, when you use data class at the top level, it makes a lot of sense to make all classes it encapsulates also data classes. That would also solve your second problem with the need of overriding equals and hashCode:
data class FlexibleProperty<T>(var value: T)
Also, there's no reason to check referential equality with ===, at least with the examples you provide.

Kotlin Any - Double missmatch

I am trying to pass arrays that can store value of Double, Int, Long or etc.
val input = arrayOf(1.3, 4.5)
val output = arrayOf(3) // Error Kotlin: Type mismatch: inferred type is Array<Int> but Array<Any> was expected
magic(input, output)
fun magic(input: Array<Any>, output: Array<Any>) {
// Do the magic
}
What type of parameters I have to use to do that?
You are probably looking for Number
fun magic(input: Array<Number>, output: Array<Number>) {
// Do the magic
}
val input = arrayOf<Number>(1.3, 4.5)
val output = arrayOf<Number>(3)
magic(input, output)
Based on your requirement, you could utilize the Number class. The documentation on the Kotlin website states that - Number is a "Superclass for all platform classes representing numeric values"
You could modify the magic function as follows -
fun magic(input: Array<Number>, output: Array<Number>) {
// Do the magic
}
Then, invoke the function by constructing the required parameters to the function -
val input = arrayOf<Number>(1.3, 4.5)
val output = arrayOf<Number>(3)
magic(input, output)

Function definition: fun vs val

I'm curious about what is the suggested way to define member functions in Kotlin. Consider these two member functions:
class A {
fun f(x: Int) = 42
val g = fun(x: Int) = 42
}
These appear to accomplish the same thing, but I found subtle differences.
The val based definition, for instance, seems to be more flexible in some scenarios. That is, I could not work out a straight forward way to compose f with other functions, but I could with g. To toy around with these definitions, I used the funKTionale library. I found that this does not compile:
val z = g andThen A::f // f is a member function
But if f were defined as a val pointing to the same function, it would compile just fine. To figure out what was going on I asked IntelliJ to explicitly define the type of ::f and g for me, and it gives me this:
val fref: KFunction1<Int, Int> = ::f
val gref: (Int) -> Int = g
So one is of type KFunction1<Int, Int>, the other is of type (Int) -> Int. It's easy to see that both represent functions of type Int -> Int.
What is the difference between these two types, and in which cases does it matter? I noticed that for top-level functions, I can compose them fine using either definition, but in order to make the aforementioned composition compile, I had to write it like so:
val z = g andThen A::f.partially1(this)
i.e. I had to partially apply it to this first.
Since I don't have to go through this hassle when using vals for functions, is there a reason why I should ever define non-Unit member functions using fun? Is there a difference in performance or semantics that I am missing?
Kotlin is all about Java interoperability and defining a function as a val will produce a completely different result in terms of the interoperability. The following Kotlin class:
class A {
fun f(x: Int) = 42
val g = fun(x: Int) = 42
}
is effectively equivalent to:
public class A {
private final Function1<Integer, Integer> gref = new Function1<Integer, Integer>() {
#Override
public Integer invoke(final Integer integer) {
return 42;
}
};
public int f(final int value) {
return 42;
}
public Function1<Integer, Integer> getG() {
return gref;
}
}
As you can see, the main differences are:
fun f is just a usual method, while val g in fact is a higher-order function that returns another function
val g involves creation of a new class which isn't good if you are targeting Android
val g requires unnecessary boxing and unboxing
val g cannot be easily invoked from java: A().g(42) in Kotlin vs new A().getG().invoke(42) in Java
UPDATE:
Regarding the A::f syntax. The compiler will generate an extra Function2<A, Integer, Integer> class for every A::f occurrence, so the following code results in two extra classes with 7 methods each:
val first = A::f
val second = A::f
Kotlin compiler isn't smart enough at the moment to optimize such kind of things. You can vote for the issue here https://youtrack.jetbrains.com/issue/KT-9831. In case you are interested, here is how each class looks in the bytecode: https://gist.github.com/nsk-mironov/fc13f2075bfa05d8a3c3
Here's some code showing how f and g are different when it comes to usage:
fun main(args: Array<String>) {
val a = A()
exe(a.g) // OK
//exe(a.f) // does not compile
exe { a.f(it) } // OK
}
fun exe(p: (Int) -> Int) {
println(p(0))
}
Where f and g are:
fun f(x: Int) = 42
val g = fun(x: Int) = 42
You can see that g is an object that can be used like a lambda, but f cannot. To use f similarly, you have to wrap it in a lambda.