I've got a strange issue that I can't seem to reso...
# ktor
l
I've got a strange issue that I can't seem to resolve. I have some data classes that look as follows:
Copy code
@Serializable
data class Tag(
    val name: String,
    @Serializable(with = TagStringSerializer::class)
    val parent: Tag? = null,
    val subTags: MutableSet<@Serializable(with = TagStringSerializer::class) Tag> = ConcurrentSet(),
    val files: MutableSet<@Serializable(with = SavedFileStringSerializer::class) SavedFile> = ConcurrentSet()
)

@Serializable
data class SavedFile(
    @Serializable(with = FileSerializer::class)
    val file: File,
    val tags: MutableSet<@Serializable(with = TagStringSerializer::class) Tag> = ConcurrentSet()
)
Of course, I don't want recursive Tags/SavedFiles in my serialization, as that would lead to infinite loops; I only serialize the names (all my custom serializers do is extract names). When I use
ApplicationCall#respond(savedFile)
, this works properly and sends
{"file":"example","tags":["test"]}
. However, when I do
ApplicationCall#respond(savedFile.tags)
, I receive an array of default-serialized tags instead of just the names, as if it's ignoring the annotation in the set:
[{"name":"test","subTags":[],"files":["example"]}]
. Even stranger, when I do just
Json.encodeToString(savedFile.tags)
, it works just fine and gives me a properly-encoded array
["test"]
. Of course, I could just create a function like
respondJson
and wrap a call to
respondText(Json.encodeToString(
, but this seems hacky and non-ideal. I'm just very confused as to why the two functions are behaving differently and how I can fix the serialization done within
ApplicationCall#respond
.
a
Can you share a sample project to troubleshoot your problem?
l
Yes, here is an example that experiences the issue. https://github.com/gecko10000/KtorSerializationExample (use
./gradlew run
to run it) Requesting
/r1
returns the properly serialized object, but requesting
/r1children
returns the children with default serialization, despite them being in a set with a specific serialization set. Moreover, requesting
/r1childrenjson
returns the proper string array and not an array of objects.
I'm not sure if I'm even doing these serialization annotations correctly - is there some sort of protocol for recursive references in serializable classes like this?
As in, how would I specify when to send all the children as serialized in the default manner, vs serialized with the special serializer?
Maybe I should just use Collection mapping functions whenever I'm not returning an object directly... but then why does
Json.encodeToString
work properly? I'm worried that I will build my application around one behavior and suddenly begin to encounter another.
any idea @Aleksei Tirman [JB]?
a
Since Ktor uses the
typeInfo
method from the reflection package to pass a type info to the
ContentNegotiation
plugin, I guess it loses the information about the serializer.
You better file an issue because that’s may be a bug.
👍 1