``` suspend fun main() { val items = MarketAPI....
# announcements
l
Copy code
suspend fun main() {
   val items = MarketAPI.getItems()

   for (index in items.indices) {
      println(items[index])
   }

   items.forEach {
      println(it)
   }
}
the return type of
MarketAPI.getItems()
is
List<Item>
where
Item
is a simple data class storing 3 Strings. the first loop works fine, the second one gives me this error: https://hastebin.com/ewuvakikuy how is this possible? how can the way i iterate through the items affect the serialization AFTER it happens? I cant even figure out where exactly it happens. the stacktrace says line 12 in main.kt, however that file only has 11 lines. I set a breakpoint and stepped through, the crash occurs as soon as i reach the second loop. but due to coroutine shenanigans the stacktrace isnt very helpful
j
Just speculating, but if you are using something like Jackson to deserialise your items, it will put things into a LinkedHashMap by default. You need to add some annotations to your MarketAPI class.
l
i do use jackson, but what do you mean with "it will put things into a LinkedHashMap" ? i am definitely returning a List<Item>
j
Your List<Item> is actually a List<LinkedHashMap> at runtime
l
even if thats the case, that doesnt explain why .forEach() breaks
j
Your return type from getItems() is List<Item> the .forEach will try to access the Item and fail since it gets back a LinkedHashMap
l
wtf
j
Try setting the type explicitly on items and you will see
c
most likely the difference is that the first cycle never tries to get an Item. it just gets whatever object is at the index and passes it directly to println which accepts Any
if that's the case, actually using it as an Item object in the first loop will crash it too
l
i tried specifying the return type explicitly instead of inferring, didnt help
c
the problem is in the deserialization, not with the return type, you need to fix that first
l
if there's a problem in the serialization, why doesnt it break much earlier
j
Make sure you are using ObjectMapper, and pass the appropriate ::class.java
The serialization is succeeding it's just not mapped to your object structure.. So it's using Lists and Maps to store the JSON
l
that sounds like awful design within ktor.io
c
basically type erasure. this list gets passed around everywhere as an untyped list because the Item element type is erased at runtime. it only breaks once it reaches forEach because the actual bytecode for it tries to do something along the lines of
Item it = (Item) items.next()
got nothing to do with ktor, just JVM things
l
so what do i need to do to fix this?
c
figure out where your List<Item> is getting deserialized and why it's instead defaulting to using a HashMap instead of Item. might be anything, from missing annotations, to wrong jackson configs
j
Are you deserialising this yourself or is some other framework doing it for you?
l
this is the MarketAPI class
i use ktor.io, which in turn uses Jackson. I also tried Gson but same problem
j
Copy code
<Map<String, Map<String, ActualDataType>>>
Problem is probably there
l
why so?
j
Because once you deserialise to Map or List, Jackson may not necessarily re-enter object mapping, it will just create LinkedHashMap instead of ActualDataType
l
why though
and how do i fix it
j
That has a pretty good set of examples
l
useful indeed, but unfortunately doesnt solve my problem
j
Your ActualDataType is a template parameter. Try replacing it with List<Item> explicitly
In Java, Jackson did some really tricky things with TypeReference class to work around type erasure. I'm not sure how Kotlin is doing it.
l
Try replacing it with List<Item> explicitly
tried, didnt help
n
what happens if you use gson instead?
l
same
n
also with explicit type List<Item>?
l
well, the error is a bit different of course, but its basically the same problem https://hastebin.com/dabememate
as said above, i tried specifying the type explicitly. it didnt help
j
https://www.baeldung.com/jackson-collection-array You are running into the issue described in part 3. I would be able to help with TypeReference, but not familiar with how Kotlin passes the type info.. But your problem is definitely that ActualDataType cannot be a template parameter. Just not sure how to communicate the correct class
c
you can use reified inline functions, although it would be better in this case to remove the type argument entirely, since this doesn't look particularly reusable either way
l
Copy code
suspend fun getItems() =
		httpClient.get<Map<String, Map<String, List<Item>>>>(root + "items").entries.first().value.entries.first().value
this works Edit: now it doesnt anymore. what is this sorcery
Copy code
private suspend inline fun <reified ActualDataType> HttpClient.getUnwrapped(url: String) = get<Map<String, Map<String, ActualDataType>>>(url).entries.first().value.entries.first().value
suspend fun getItems() = httpClient.getUnwrapped<List<Item>>(root + "items")
this doesn't. why?
it would be better in this case to remove the type argument entirely, since this doesn't look particularly reusable either way
what do you mean? by using a type argument i can use this function for literally every api call
c
is every api call wrapped in this way?
l
yes
thats why made this stupid function
c
oh
j
My advice is to model the entire structure as data classes and then just deserialise to the top level type, let Jackson take care of the details. It may take you a few hours to figure out but it's definitely possible. Remember Map above just maps to object with the keys as properties
l
sry i dont understand what you're suggesting. my data in this case is just
List<Item>
where
Item
is a data class containing 3 string variables
oh wait i get it
j
create three data classes.. top contains payload, payload contains items, items holds your data
l
yeah im afraid thats what i'll have to do
j
then you only need to pass <Top> to the get function on httpclient
top.payload.items will retrieve your list.. it will be much easier to work with