summaryrefslogtreecommitdiff
path: root/formats/json
diff options
context:
space:
mode:
authorLeonid Startsev <sandwwraith@users.noreply.github.com>2023-07-14 14:59:42 +0200
committerGitHub <noreply@github.com>2023-07-14 14:59:42 +0200
commit3191884b106a9737df680b24b7fc0673d8b154f0 (patch)
tree60ce025e03a784f61a3b615efd98df6bfaf12d47 /formats/json
parentb8de86f0e351f1099d2afb03ff92e2ef6256cbc7 (diff)
downloadkotlinx.serialization-3191884b106a9737df680b24b7fc0673d8b154f0.tar.gz
Fix error triggered by 'consume leading class discriminator' polymorphic parsing optimization: (#2362)
In case of an empty object, no exception should be thrown, it should be treated as missing class discriminator. peekString() correctly handles this case. Also fixes misplaced calls in lenient vs non-lenient mode. Fixes #2353
Diffstat (limited to 'formats/json')
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonDecoder.kt2
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/AbstractJsonLexer.kt6
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/JsonLexer.kt2
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/StringJsonLexer.kt19
4 files changed, 16 insertions, 13 deletions
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonDecoder.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonDecoder.kt
index 2d4517d9..6cedabce 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonDecoder.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonDecoder.kt
@@ -71,7 +71,7 @@ internal open class StreamingJsonDecoder(
}
val discriminator = deserializer.descriptor.classDiscriminator(json)
- val type = lexer.consumeLeadingMatchingValue(discriminator, configuration.isLenient)
+ val type = lexer.peekLeadingMatchingValue(discriminator, configuration.isLenient)
var actualSerializer: DeserializationStrategy<Any>? = null
if (type != null) {
actualSerializer = deserializer.findPolymorphicSerializerOrNull(this, type)
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/AbstractJsonLexer.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/AbstractJsonLexer.kt
index 58ed68d6..3e2c6ad4 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/AbstractJsonLexer.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/AbstractJsonLexer.kt
@@ -286,7 +286,7 @@ internal abstract class AbstractJsonLexer {
return current
}
- abstract fun consumeLeadingMatchingValue(keyToMatch: String, isLenient: Boolean): String?
+ abstract fun peekLeadingMatchingValue(keyToMatch: String, isLenient: Boolean): String?
fun peekString(isLenient: Boolean): String? {
val token = peekNextToken()
@@ -301,6 +301,10 @@ internal abstract class AbstractJsonLexer {
return string
}
+ fun discardPeeked() {
+ peekedString = null
+ }
+
open fun indexOf(char: Char, startPos: Int) = source.indexOf(char, startPos)
open fun substring(startPos: Int, endPos: Int) = source.substring(startPos, endPos)
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/JsonLexer.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/JsonLexer.kt
index 2881fd79..85ef13ab 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/JsonLexer.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/JsonLexer.kt
@@ -179,7 +179,7 @@ internal class ReaderJsonLexer(
}
// Can be carefully implemented but postponed for now
- override fun consumeLeadingMatchingValue(keyToMatch: String, isLenient: Boolean): String? = null
+ override fun peekLeadingMatchingValue(keyToMatch: String, isLenient: Boolean): String? = null
fun release() {
CharArrayPoolBatchSize.release(buffer)
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/StringJsonLexer.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/StringJsonLexer.kt
index 24c9a67c..717932c7 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/StringJsonLexer.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/StringJsonLexer.kt
@@ -101,21 +101,20 @@ internal class StringJsonLexer(override val source: String) : AbstractJsonLexer(
(if (isLenient) consumeStringLenient() else consumeString()).chunked(BATCH_SIZE).forEach(consumeChunk)
}
- override fun consumeLeadingMatchingValue(keyToMatch: String, isLenient: Boolean): String? {
+ override fun peekLeadingMatchingValue(keyToMatch: String, isLenient: Boolean): String? {
val positionSnapshot = currentPosition
try {
- // Malformed JSON, bailout
- if (consumeNextToken() != TC_BEGIN_OBJ) return null
- val firstKey = if (isLenient) consumeKeyString() else consumeStringLenientNotNull()
- if (firstKey == keyToMatch) {
- if (consumeNextToken() != TC_COLON) return null
- val result = if (isLenient) consumeString() else consumeStringLenientNotNull()
- return result
- }
- return null
+ if (consumeNextToken() != TC_BEGIN_OBJ) return null // Malformed JSON, bailout
+ val firstKey = peekString(isLenient)
+ if (firstKey != keyToMatch) return null
+ discardPeeked() // consume firstKey
+ if (consumeNextToken() != TC_COLON) return null
+ return peekString(isLenient)
} finally {
// Restore the position
currentPosition = positionSnapshot
+ discardPeeked()
}
}
}
+