-
Notifications
You must be signed in to change notification settings - Fork 624
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve polymorphic deserialization optimization: (#2481)
Previously, when discriminator was found as the first key in Json, but there was no deserializer for it, we still fell back to a slow path with JsonTree. It was actually meaningless because a slow path always throws exception when a serializer is not found. Such behavior led to unnecessary memory pressure & consumption in exceptional cases (see linked ticket for details). Also make polymorphic deserialization exception messages more meaningful and make them more consistent with serialization ones. Also fix behavior when the actual discriminator value is JsonNull (it should be treated as missing, not as "null" string). Fixes #2478
- Loading branch information
1 parent
cf57414
commit 7d4bb2a
Showing
9 changed files
with
134 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
...monTest/src/kotlinx/serialization/features/PolymorphicDeserializationErrorMessagesTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. | ||
*/ | ||
|
||
package kotlinx.serialization.features | ||
|
||
import kotlinx.serialization.* | ||
import kotlinx.serialization.json.* | ||
import kotlin.test.* | ||
|
||
class PolymorphicDeserializationErrorMessagesTest : JsonTestBase() { | ||
@Serializable | ||
class DummyData(@Polymorphic val a: Any) | ||
|
||
@Serializable | ||
class Holder(val d: DummyData) | ||
|
||
// TODO: remove this after #2480 is merged | ||
private fun checkSerializationException(action: () -> Unit, assertions: SerializationException.(String) -> Unit) { | ||
val e = assertFailsWith(SerializationException::class, action) | ||
assertNotNull(e.message) | ||
e.assertions(e.message!!) | ||
} | ||
|
||
@Test | ||
fun testNotRegisteredMessage() = parametrizedTest { mode -> | ||
val input = """{"d":{"a":{"type":"my.Class", "value":42}}}""" | ||
checkSerializationException({ | ||
default.decodeFromString<Holder>(input, mode) | ||
}, { message -> | ||
// ReaderJsonLexer.peekLeadingMatchingValue is not implemented, so first-key optimization is not working for streaming yet. | ||
if (mode == JsonTestingMode.STREAMING) | ||
assertContains(message, "Unexpected JSON token at offset 10: Serializer for subclass 'my.Class' is not found in the polymorphic scope of 'Any' at path: \$.d.a") | ||
else | ||
assertContains(message, "Serializer for subclass 'my.Class' is not found in the polymorphic scope of 'Any'") | ||
}) | ||
} | ||
|
||
@Test | ||
fun testDiscriminatorMissingNoDefaultMessage() = parametrizedTest { mode -> | ||
val input = """{"d":{"a":{"value":42}}}""" | ||
checkSerializationException({ | ||
default.decodeFromString<Holder>(input, mode) | ||
}, { message -> | ||
// Always slow path when discriminator is missing, so no position and path | ||
assertContains(message, "Class discriminator was missing and no default serializers were registered in the polymorphic scope of 'Any'") | ||
}) | ||
} | ||
|
||
@Test | ||
fun testClassDiscriminatorIsNull() = parametrizedTest { mode -> | ||
val input = """{"d":{"a":{"type":null, "value":42}}}""" | ||
checkSerializationException({ | ||
default.decodeFromString<Holder>(input, mode) | ||
}, { message -> | ||
// Always slow path when discriminator is missing, so no position and path | ||
assertContains(message, "Class discriminator was missing and no default serializers were registered in the polymorphic scope of 'Any'") | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters