summaryrefslogtreecommitdiff
path: root/formats/json
diff options
context:
space:
mode:
authorVsevolod Tolstopyatov <qwwdfsad@gmail.com>2021-06-15 16:23:44 +0300
committerGitHub <noreply@github.com>2021-06-15 16:23:44 +0300
commitb051cb3f846dbcfc8cdcdd863c5f4d5978fa7941 (patch)
tree2c03e18cfab0a96df9530dbdf21b6286e52e27f9 /formats/json
parent2ef890c8a2c7367c741efb4613aa3195eebe24e1 (diff)
downloadkotlinx.serialization-b051cb3f846dbcfc8cdcdd863c5f4d5978fa7941.tar.gz
Throw JsonDecodingException instead of ClassCastException during unexpected null in TreeJsonDecoder (#1550)
Diffstat (limited to 'formats/json')
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonDecoder.kt32
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/json/JsonParserFailureModesTest.kt15
2 files changed, 33 insertions, 14 deletions
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonDecoder.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonDecoder.kt
index e14a04c4..78017bb5 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonDecoder.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonDecoder.kt
@@ -72,7 +72,7 @@ private sealed class AbstractJsonTreeDecoder(
override fun decodeNotNullMark(): Boolean = currentObject() !is JsonNull
- protected open fun getValue(tag: String): JsonPrimitive {
+ protected fun getPrimitiveValue(tag: String): JsonPrimitive {
val currentElement = currentElement(tag)
return currentElement as? JsonPrimitive ?: throw JsonDecodingException(
-1,
@@ -83,16 +83,16 @@ private sealed class AbstractJsonTreeDecoder(
protected abstract fun currentElement(tag: String): JsonElement
override fun decodeTaggedEnum(tag: String, enumDescriptor: SerialDescriptor): Int =
- enumDescriptor.getJsonNameIndexOrThrow(json, getValue(tag).content)
+ enumDescriptor.getJsonNameIndexOrThrow(json, getPrimitiveValue(tag).content)
override fun decodeTaggedNull(tag: String): Nothing? = null
override fun decodeTaggedNotNullMark(tag: String): Boolean = currentElement(tag) !== JsonNull
override fun decodeTaggedBoolean(tag: String): Boolean {
- val value = getValue(tag)
+ val value = getPrimitiveValue(tag)
if (!json.configuration.isLenient) {
- val literal = value as JsonLiteral
+ val literal = value.asLiteral("boolean")
if (literal.isString) throw JsonDecodingException(
-1, "Boolean literal for key '$tag' should be unquoted.\n$lenientHint", currentObject().toString()
)
@@ -102,36 +102,36 @@ private sealed class AbstractJsonTreeDecoder(
}
}
- override fun decodeTaggedByte(tag: String) = getValue(tag).primitive("byte") {
+ override fun decodeTaggedByte(tag: String) = getPrimitiveValue(tag).primitive("byte") {
val result = int
if (result in Byte.MIN_VALUE..Byte.MAX_VALUE) result.toByte()
else null
}
- override fun decodeTaggedShort(tag: String) = getValue(tag).primitive("short") {
+ override fun decodeTaggedShort(tag: String) = getPrimitiveValue(tag).primitive("short") {
val result = int
if (result in Short.MIN_VALUE..Short.MAX_VALUE) result.toShort()
else null
}
- override fun decodeTaggedInt(tag: String) = getValue(tag).primitive("int") { int }
- override fun decodeTaggedLong(tag: String) = getValue(tag).primitive("long") { long }
+ override fun decodeTaggedInt(tag: String) = getPrimitiveValue(tag).primitive("int") { int }
+ override fun decodeTaggedLong(tag: String) = getPrimitiveValue(tag).primitive("long") { long }
override fun decodeTaggedFloat(tag: String): Float {
- val result = getValue(tag).primitive("float") { float }
+ val result = getPrimitiveValue(tag).primitive("float") { float }
val specialFp = json.configuration.allowSpecialFloatingPointValues
if (specialFp || result.isFinite()) return result
throw InvalidFloatingPointDecoded(result, tag, currentObject().toString())
}
override fun decodeTaggedDouble(tag: String): Double {
- val result = getValue(tag).primitive("double") { double }
+ val result = getPrimitiveValue(tag).primitive("double") { double }
val specialFp = json.configuration.allowSpecialFloatingPointValues
if (specialFp || result.isFinite()) return result
throw InvalidFloatingPointDecoded(result, tag, currentObject().toString())
}
- override fun decodeTaggedChar(tag: String): Char = getValue(tag).primitive("char") { content.single() }
+ override fun decodeTaggedChar(tag: String): Char = getPrimitiveValue(tag).primitive("char") { content.single() }
private inline fun <T: Any> JsonPrimitive.primitive(primitive: String, block: JsonPrimitive.() -> T?): T {
try {
@@ -146,9 +146,9 @@ private sealed class AbstractJsonTreeDecoder(
}
override fun decodeTaggedString(tag: String): String {
- val value = getValue(tag)
+ val value = getPrimitiveValue(tag)
if (!json.configuration.isLenient) {
- val literal = value as JsonLiteral
+ val literal = value.asLiteral("string")
if (!literal.isString) throw JsonDecodingException(
-1, "String literal for key '$tag' should be quoted.\n$lenientHint", currentObject().toString()
)
@@ -157,9 +157,13 @@ private sealed class AbstractJsonTreeDecoder(
return value.content
}
+ private fun JsonPrimitive.asLiteral(type: String): JsonLiteral {
+ return this as? JsonLiteral ?: throw JsonDecodingException(-1, "Unexpected 'null' when $type was expected")
+ }
+
@OptIn(ExperimentalUnsignedTypes::class)
override fun decodeTaggedInline(tag: String, inlineDescriptor: SerialDescriptor): Decoder =
- if (inlineDescriptor.isUnsignedNumber) JsonDecoderForUnsignedTypes(JsonLexer(getValue(tag).content), json)
+ if (inlineDescriptor.isUnsignedNumber) JsonDecoderForUnsignedTypes(JsonLexer(getPrimitiveValue(tag).content), json)
else super.decodeTaggedInline(tag, inlineDescriptor)
}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/JsonParserFailureModesTest.kt b/formats/json/commonTest/src/kotlinx/serialization/json/JsonParserFailureModesTest.kt
index e9ac4375..87b0f358 100644
--- a/formats/json/commonTest/src/kotlinx/serialization/json/JsonParserFailureModesTest.kt
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/JsonParserFailureModesTest.kt
@@ -143,4 +143,19 @@ class JsonParserFailureModesTest : JsonTestBase() {
assertFailsWith<JsonDecodingException> { default.decodeFromString<Holder>("""{"id":a}""", it) }
assertFailsWith<JsonDecodingException> { default.decodeFromString<Holder>("""{"id":-a}""", it) }
}
+
+
+ @Serializable
+ data class BooleanWrapper(val b: Boolean)
+
+ @Serializable
+ data class StringWrapper(val s: String)
+
+ @Test
+ fun testUnexpectedNull() = parametrizedTest {
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<BooleanWrapper>("""{"b":{"b":"b"}}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<BooleanWrapper>("""{"b":null}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<StringWrapper>("""{"s":{"s":"s"}}""", it) }
+ assertFailsWith<JsonDecodingException> { default.decodeFromString<StringWrapper>("""{"s":null}""", it) }
+ }
}