diff options
Diffstat (limited to 'formats/json-tests')
427 files changed, 15454 insertions, 0 deletions
diff --git a/formats/json-tests/build.gradle.kts b/formats/json-tests/build.gradle.kts new file mode 100644 index 00000000..6be0a3a7 --- /dev/null +++ b/formats/json-tests/build.gradle.kts @@ -0,0 +1,60 @@ +/* + * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +import Java9Modularity.configureJava9ModuleInfo +import org.jetbrains.kotlin.gradle.targets.js.testing.* + +plugins { + kotlin("multiplatform") + kotlin("plugin.serialization") +} + +apply(from = rootProject.file("gradle/native-targets.gradle")) +apply(from = rootProject.file("gradle/configure-source-sets.gradle")) + +// disable kover tasks because there are no non-test classes in the project +tasks.named("koverHtmlReport") { + enabled = false +} +tasks.named("koverXmlReport") { + enabled = false +} +tasks.named("koverVerify") { + enabled = false +} + +kotlin { + sourceSets { + configureEach { + languageSettings { + optIn("kotlinx.serialization.internal.CoreFriendModuleApi") + optIn("kotlinx.serialization.json.internal.JsonFriendModuleApi") + } + } + val commonTest by getting { + dependencies { + api(project(":kotlinx-serialization-json")) + api(project(":kotlinx-serialization-json-okio")) + implementation("com.squareup.okio:okio:${property("okio_version")}") + } + } + + val jvmTest by getting { + dependencies { + implementation("com.google.code.gson:gson:2.8.5") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${property("coroutines_version")}") + } + } + } +} + +project.configureJava9ModuleInfo() + +// TODO: Remove this after okio will be updated to the version with 1.9.20 stdlib dependency +configurations.all { + resolutionStrategy.eachDependency { + if (requested.name == "kotlin-stdlib-wasm") { + useTarget("org.jetbrains.kotlin:kotlin-stdlib-wasm-js:${requested.version}") + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/ClassWithMultipleMasksTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/ClassWithMultipleMasksTest.kt new file mode 100644 index 00000000..cc0158c1 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/ClassWithMultipleMasksTest.kt @@ -0,0 +1,85 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization + +import kotlinx.serialization.json.Json +import kotlin.test.* + +class ClassWithMultipleMasksTest { + + /* + * Plugin generates int mask for each 32 fields. + * This test ensures that mask is properly generated when fields count is greater than 32. + */ + @Serializable + data class BigDummyData( + val regular: String, + @SerialName("field0") val field0: String? = null, + @SerialName("field1") val field1: String? = null, + @SerialName("field2") val field2: String? = null, + @SerialName("field3") val field3: String? = null, + @SerialName("field4") val field4: String? = null, + @SerialName("field5") val field5: String? = null, + @SerialName("field6") val field6: String? = null, + @SerialName("field7") val field7: String? = null, + @SerialName("field8") val field8: String? = null, + @SerialName("field9") val field9: String? = null, + @SerialName("field10") val field10: String? = null, + @SerialName("field11") val field11: String? = null, + @SerialName("field12") val field12: String? = null, + @SerialName("field13") val field13: String? = null, + @SerialName("field14") val field14: String? = null, + @SerialName("field15") val field15: String? = null, + @SerialName("field16") val field16: String? = null, + @SerialName("field17") val field17: String? = null, + @SerialName("field18") val field18: String? = null, + @SerialName("field19") val field19: String? = null, + @SerialName("field20") val field20: String? = null, + @SerialName("field21") val field21: String? = null, + @SerialName("field22") val field22: String? = null, + @SerialName("field23") val field23: String? = null, + @SerialName("field24") val field24: String? = null, + @SerialName("field25") val field25: String? = null, + @SerialName("field26") val field26: String? = null, + @SerialName("field27") val field27: String? = null, + @SerialName("field28") val field28: String? = null, + @SerialName("field29") val field29: String? = null, + @SerialName("field30") val field30: String? = null, + @SerialName("field31") val field31: String? = null, + @SerialName("field32") val field32: String? = null, + @SerialName("field33") val field33: String? = null, + @SerialName("field34") val field34: String? = null, + @SerialName("field35") val field35: String? = null, + @SerialName("field36") val field36: String? = null, + @SerialName("field37") val field37: String? = null, + @SerialName("field38") val field38: String? = null, + @SerialName("field39") val field39: String? = null, + @SerialName("field40") val field40: String? = "b", + @Required val requiredLast: String = "required" + ) + + @Test + fun testMoreThan32Fields() { + val data = BigDummyData("a") + val message = Json.encodeToString(BigDummyData.serializer(), data) + println(message) + val restored = Json.decodeFromString(BigDummyData.serializer(), """{"regular": "0","requiredLast":"r"}""") + with(restored) { + assertEquals("0", regular) + assertEquals("b", field40) + assertEquals(null, field39) + assertEquals("r", requiredLast) + } + + val restored2 = Json.decodeFromString(BigDummyData.serializer(), """{"regular": "0", "field39":"f","requiredLast":"required"}""") + with(restored2) { + assertEquals("0", regular) + assertEquals("b", field40) + assertEquals("f", field39) + assertEquals("required", requiredLast) + } + assertFailsWith<SerializationException> { Json.decodeFromString(BigDummyData.serializer(), """{"regular": "0"}""") } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/EncodingCollectionsTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/EncodingCollectionsTest.kt new file mode 100644 index 00000000..cd077e04 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/EncodingCollectionsTest.kt @@ -0,0 +1,27 @@ +package kotlinx.serialization + +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlin.test.* + +class EncodingCollectionsTest { + object ListSerializer : KSerializer<List<String>> { + override val descriptor: SerialDescriptor = ListSerializer(String.serializer()).descriptor + + override fun serialize(encoder: Encoder, value: List<String>) { + encoder.encodeCollection(descriptor, value) { index, item -> + encodeStringElement(descriptor, index, item) + } + } + + override fun deserialize(decoder: Decoder): List<String> = throw NotImplementedError() + } + + @Test + fun testEncoding() { + assertEquals("""["Hello","World!"]""", Json.encodeToString(ListSerializer, listOf("Hello", "World!"))) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/EncodingExtensionsTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/EncodingExtensionsTest.kt new file mode 100644 index 00000000..73d3319a --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/EncodingExtensionsTest.kt @@ -0,0 +1,40 @@ +package kotlinx.serialization + +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlin.test.* + +class EncodingExtensionsTest { + + @Serializable(with = BoxSerializer::class) + class Box(val i: Int) + + object BoxSerializer : KSerializer<Box> { + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Box") { + element<Int>("i") + } + + override fun serialize(encoder: Encoder, value: Box) { + encoder.encodeStructure(descriptor) { + throw ArithmeticException() + } + } + + override fun deserialize(decoder: Decoder): Box { + decoder.decodeStructure(descriptor) { + throw ArithmeticException() + } + } + } + + @Test + fun testEncodingExceptionNotSwallowed() { + assertFailsWith<ArithmeticException> { Json.encodeToString(Box(1)) } + } + + @Test + fun testDecodingExceptionNotSwallowed() { + assertFailsWith<ArithmeticException> { Json.decodeFromString<Box>("""{"i":1}""") } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/EnumSerializationTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/EnumSerializationTest.kt new file mode 100644 index 00000000..d361bbb6 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/EnumSerializationTest.kt @@ -0,0 +1,132 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization + +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.JsonTestBase +import kotlinx.serialization.test.* +import kotlin.test.* + +class EnumSerializationTest : JsonTestBase() { + + @Serializable + enum class RegularEnum { + VALUE + } + + @Serializable + data class Regular(val a: RegularEnum) + + @Serializable + data class RegularNullable(val a: RegularEnum?) + + @Serializable + @SerialName("custom_enum") + private enum class CustomEnum { + @SerialName("foo_a") + FooA, + + @SerialName("foo_b") + @Id(10) + FooB + } + + @Serializable + private data class WithCustomEnum(val c: CustomEnum) + + @Serializable(CustomEnumSerializer::class) + private enum class WithCustom { + @SerialName("1") + ONE, + @SerialName("2") + TWO + } + + private class CustomEnumSerializer : KSerializer<WithCustom> { + override val descriptor: SerialDescriptor = buildSerialDescriptor("WithCustom", SerialKind.ENUM) { + element("1", buildSerialDescriptor("WithCustom.1", StructureKind.OBJECT)) + element("2", buildSerialDescriptor("WithCustom.2", StructureKind.OBJECT)) + } + + override fun serialize(encoder: Encoder, value: WithCustom) { + encoder.encodeInt(value.ordinal + 1) + } + + override fun deserialize(decoder: Decoder): WithCustom { + return WithCustom.values()[decoder.decodeInt() - 1] + } + } + + @Serializable + private data class CustomInside(val inside: WithCustom) + + @Test + fun testEnumSerialization() = + assertJsonFormAndRestored( + WithCustomEnum.serializer(), + WithCustomEnum(CustomEnum.FooB), + """{"c":"foo_b"}""", + default + ) + + @Test + fun testEnumWithCustomSerializers() = + assertJsonFormAndRestored( + CustomInside.serializer(), + CustomInside(WithCustom.TWO), """{"inside":2}""" + ) + + + @Test + fun testHasMeaningfulToString() { + val regular = Regular.serializer().descriptor.toString() + assertEquals( + "kotlinx.serialization.EnumSerializationTest.Regular(a: kotlinx.serialization.EnumSerializationTest.RegularEnum)", + regular + ) + val regularNullable = RegularNullable.serializer().descriptor.toString() + assertEquals( + "kotlinx.serialization.EnumSerializationTest.RegularNullable(a: kotlinx.serialization.EnumSerializationTest.RegularEnum?)", + regularNullable + ) + // slightly differs from previous one + val regularNullableJoined = RegularNullable.serializer().descriptor.elementDescriptors.joinToString() + assertEquals("kotlinx.serialization.EnumSerializationTest.RegularEnum(VALUE)?", regularNullableJoined) + + val regularEnum = RegularEnum.serializer().descriptor.toString() + assertEquals("kotlinx.serialization.EnumSerializationTest.RegularEnum(VALUE)", regularEnum) + } + + + @Test + fun testHasMeaningfulHashCode() { + val a = Regular.serializer().descriptor.hashCode() + val b = RegularNullable.serializer().descriptor.hashCode() + val c = RegularEnum.serializer().descriptor.hashCode() + assertTrue(setOf(a, b, c).size == 3, ".hashCode must give different result for different descriptors") + } + + enum class MyEnum { + A, B, C; + } + + @Serializable + @SerialName("kotlinx.serialization.EnumSerializationTest.MyEnum") + enum class MyEnum2 { + A, B, C; + } + + @Serializable + class Wrapper(val a: MyEnum) + + @Test + fun testStructurallyEqualDescriptors() { + val libraryGenerated = Wrapper.serializer().descriptor.getElementDescriptor(0) + val codeGenerated = MyEnum2.serializer().descriptor + assertEquals(libraryGenerated::class, codeGenerated::class) + libraryGenerated.assertDescriptorEqualsTo(codeGenerated) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/GenericSerializersOnFileTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/GenericSerializersOnFileTest.kt new file mode 100644 index 00000000..c3003ca9 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/GenericSerializersOnFileTest.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:UseSerializers(GenericSerializersOnFileTest.MySerializer::class) + +package kotlinx.serialization + +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.Json +import kotlin.test.Test +import kotlin.test.assertEquals + +class GenericSerializersOnFileTest { + data class GenericClass<T>(val t: T) + + @Serializable + data class Holder(val notnull: GenericClass<String>, val nullable: GenericClass<String>?) + + class MySerializer<E>(val tSer: KSerializer<E>) : KSerializer<GenericClass<E>> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("my int descriptor", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: GenericClass<E>) { + encoder.encodeString(value.t as String) + } + + @Suppress("UNCHECKED_CAST") + override fun deserialize(decoder: Decoder): GenericClass<E> { + return GenericClass(decoder.decodeString() as E) + } + } + + @Test + fun testSerialize() { + assertEquals( + """{"notnull":"Not Null","nullable":null}""", + Json.encodeToString(Holder(notnull = GenericClass("Not Null"), nullable = null)) + ) + assertEquals( + """{"notnull":"Not Null","nullable":"Nullable"}""", + Json.encodeToString(Holder(notnull = GenericClass("Not Null"), nullable = GenericClass("Nullable"))) + ) + } + + @Test + fun testDeserialize() { + assertEquals( + Holder(notnull = GenericClass("Not Null"), nullable = null), + Json.decodeFromString("""{"notnull":"Not Null","nullable":null}""") + ) + assertEquals( + Holder(notnull = GenericClass("Not Null"), nullable = GenericClass("Nullable")), + Json.decodeFromString("""{"notnull":"Not Null","nullable":"Nullable"}""") + ) + } + + +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/JsonOverwriteKeyTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/JsonOverwriteKeyTest.kt new file mode 100644 index 00000000..b2425a1f --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/JsonOverwriteKeyTest.kt @@ -0,0 +1,36 @@ +package kotlinx.serialization + +import kotlinx.serialization.builtins.* +import kotlinx.serialization.json.* +import kotlin.test.* + +class JsonOverwriteKeyTest : JsonTestBase() { + private val json = Json + + @Serializable + data class Data(val a: Int) + + @Serializable + data class Updatable(val d: Data) + + @Test + fun testLatestValueWins() { + val parsed: Updatable = default.decodeFromString("""{"d":{"a":"42"},"d":{"a":43}}""") + assertEquals(Data(43), parsed.d) + } + + @Serializable + data class WrappedMap<T>(val mp: Map<String, T>) + + @Test + fun testLatestKeyInMap() { + val parsed = json.decodeFromString(WrappedMap.serializer(Int.serializer()), """{"mp": { "x" : 23, "x" : 42, "y": 4 }}""") + assertEquals(WrappedMap(mapOf("x" to 42, "y" to 4)), parsed) + } + + @Test + fun testLastestListValueInMap() { + val parsed = json.decodeFromString(WrappedMap.serializer(ListSerializer(Int.serializer())), """{"mp": { "x" : [23], "x" : [42], "y": [4] }}""") + assertEquals(WrappedMap(mapOf("x" to listOf(42), "y" to listOf(4))), parsed) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/JsonPathTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/JsonPathTest.kt new file mode 100644 index 00000000..8d31ba22 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/JsonPathTest.kt @@ -0,0 +1,164 @@ +/* + * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization + +import kotlinx.serialization.json.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class JsonPathTest : JsonTestBase() { + + @Serializable + class Outer(val a: Int, val i: Inner) + + @Serializable + class Inner(val a: Int, val b: String, val c: List<String>, val d: Map<Int, Box>) + + @Serializable + class Box(val s: String) + + @Test + fun testBasicError() { + expectPath("$.a") { Json.decodeFromString<Outer>("""{"a":foo}""") } + expectPath("$.i") { Json.decodeFromString<Outer>("""{"a":42, "i":[]}""") } + expectPath("$.i.b") { Json.decodeFromString<Outer>("""{"a":42, "i":{"a":43, "b":42}""") } + expectPath("$.i.b") { Json.decodeFromString<Outer>("""{"a":42, "i":{"b":42}""") } + } + + @Test + fun testMissingKey() { + expectPath("$.i.d['1']") { Json.decodeFromString<Outer>("""{"a":42, "i":{"d":{1:{}}""") } + } + + @Test + fun testUnknownKeyIsProperlyReported() { + expectPath("$.i") { Json.decodeFromString<Outer>("""{"a":42, "i":{"foo":42}""") } + expectPath("$") { Json.decodeFromString<Outer>("""{"x":{}, "a": 42}""") } + // The only place we have misattribution in + // Json.decodeFromString<Outer>("""{"a":42, "x":{}}""") + } + + @Test + fun testMalformedRootObject() { + expectPath("$") { Json.decodeFromString<Outer>("""{{""") } + } + + @Test + fun testArrayIndex() { + expectPath("$.i.c[1]") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "c": ["a", 2]}""") } + expectPath("$[2]") { Json.decodeFromString<List<String>>("""["a", "2", 3]""") } + } + + @Test + fun testArrayIndexMalformedArray() { + // Also zeroes as we cannot distinguish what exactly wen wrong is such cases + expectPath("$.i.c[0]") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "c": [[""") } + expectPath("$[0]") { Json.decodeFromString<List<String>>("""[[""") } + // But we can here + expectPath("$.i.c\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "c": {}}}""") } + expectPath("$\n") { Json.decodeFromString<List<String>>("""{""") } + } + + @Test + fun testMapKey() { + expectPath("$.i.d\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "d": {"foo": {}}""") } + expectPath("$.i.d\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "d": {42: {"s":"s"}, 42.0:{}}""") } + expectPath("$\n") { Json.decodeFromString<Map<Int, String>>("""{"foo":"bar"}""") } + expectPath("$\n") { Json.decodeFromString<Map<Int, String>>("""{42:"bar", "foo":"bar"}""") } + expectPath("$['42']['foo']") { Json.decodeFromString<Map<Int, Map<String, Int>>>("""{42: {"foo":"bar"}""") } + } + + @Test + fun testMalformedMap() { + expectPath("$.i.d\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "d": []""") } + expectPath("$\n") { Json.decodeFromString<Map<Int, String>>("""[]""") } + } + + @Test + fun testMapValue() { + expectPath("$.i.d['42']\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "d": {42: {"xx":"bar"}}""") } + expectPath("$.i.d['43']\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "d": {42: {"s":"s"}, 43: {"xx":"bar"}}}""") } + expectPath("$['239']") { Json.decodeFromString<Map<Int, String>>("""{239:bar}""") } + } + + @Serializable + class Fp(val d: Double) + + @Test + fun testInvalidFp() { + expectPath("$.d") { Json.decodeFromString<Fp>("""{"d": NaN}""") } + } + + @Serializable + class EH(val e: E) + enum class E + + @Test + fun testUnknownEnum() { + expectPath("$.e") { Json.decodeFromString<EH>("""{"e": "foo"}""") } + } + + @Serializable + @SerialName("f") + sealed class Sealed { + + @Serializable + @SerialName("n") + class Nesting(val f: Sealed) : Sealed() + + @Serializable + @SerialName("b") + class Box(val s: String) : Sealed() + + @Serializable + @SerialName("d") + class DoubleNesting(val f: Sealed, val f2: Sealed) : Sealed() + } + + // TODO use non-array polymorphism when https://github.com/Kotlin/kotlinx.serialization/issues/1839 is fixed + @Test + fun testHugeNestingToCheckResize() = jvmOnly { + val json = Json { useArrayPolymorphism = true } + var outer = Sealed.Nesting(Sealed.Box("value")) + repeat(100) { + outer = Sealed.Nesting(outer) + } + val str = json.encodeToString(Sealed.serializer(), outer) + // throw-away data + json.decodeFromString(Sealed.serializer(), str) + + val malformed = str.replace("\"value\"", "42") + val expectedPath = "$" + ".value.f".repeat(101) + ".value.s" + expectPath(expectedPath) { json.decodeFromString(Sealed.serializer(), malformed) } + } + + @Test + fun testDoubleNesting() = jvmOnly { + val json = Json { useArrayPolymorphism = true } + var outer1 = Sealed.Nesting(Sealed.Box("correct")) + repeat(64) { + outer1 = Sealed.Nesting(outer1) + } + + var outer2 = Sealed.Nesting(Sealed.Box("incorrect")) + repeat(33) { + outer2 = Sealed.Nesting(outer2) + } + + val str = json.encodeToString(Sealed.serializer(), Sealed.DoubleNesting(outer1, outer2)) + // throw-away data + json.decodeFromString(Sealed.serializer(), str) + + val malformed = str.replace("\"incorrect\"", "42") + val expectedPath = "$.value.f2" + ".value.f".repeat(34) + ".value.s" + expectPath(expectedPath) { json.decodeFromString(Sealed.serializer(), malformed) } + } + + private inline fun expectPath(path: String, block: () -> Unit) { + val message = runCatching { block() } + .exceptionOrNull()!!.message!! + assertContains(message, path) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/NotNullSerializersCompatibilityOnFileTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/NotNullSerializersCompatibilityOnFileTest.kt new file mode 100644 index 00000000..e0903470 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/NotNullSerializersCompatibilityOnFileTest.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +@file:UseSerializers(NotNullSerializersCompatibilityOnFileTest.NonNullableIntSerializer::class) +@file:UseContextualSerialization(NotNullSerializersCompatibilityOnFileTest.FileContextualType::class) + +package kotlinx.serialization + +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.contextual +import kotlin.test.* + +class NotNullSerializersCompatibilityOnFileTest { + data class FileContextualType(val text: String) + + object FileContextualSerializer : KSerializer<FileContextualType> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("FileContextualSerializer", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: FileContextualType) { + return encoder.encodeString(value.text) + } + + override fun deserialize(decoder: Decoder): FileContextualType { + return FileContextualType(decoder.decodeString()) + } + } + + @Serializable + data class FileContextualHolder(val nullable: FileContextualType?, val nonNullable: FileContextualType) + + + data class ContextualType(val text: String) + + object ContextualSerializer : KSerializer<ContextualType> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("FileContextualSerializer", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: ContextualType) { + return encoder.encodeString(value.text) + } + + override fun deserialize(decoder: Decoder): ContextualType { + return ContextualType(decoder.decodeString()) + } + } + + @Serializable + data class ContextualHolder(@Contextual val nullable: ContextualType?, @Contextual val nonNullable: ContextualType) + + + @Serializable + data class Holder(val nullable: Int?, val nonNullable: Int) + + object NonNullableIntSerializer : KSerializer<Int> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("NotNullIntSerializer", PrimitiveKind.INT) + + override fun serialize(encoder: Encoder, value: Int) { + return encoder.encodeInt(value + 2) + } + + override fun deserialize(decoder: Decoder): Int { + return (decoder.decodeInt() - 2) + } + } + + @Test + fun testFileLevel() { + assertEquals("""{"nullable":null,"nonNullable":52}""", Json.encodeToString(Holder(nullable = null, nonNullable = 50))) + assertEquals("""{"nullable":2,"nonNullable":2}""", Json.encodeToString(Holder(nullable = 0, nonNullable = 0))) + assertEquals("""{"nullable":12,"nonNullable":52}""", Json.encodeToString(Holder(nullable = 10, nonNullable = 50))) + + assertEquals(Holder(nullable = 0, nonNullable = 50), Json.decodeFromString("""{"nullable":2,"nonNullable":52}""")) + assertEquals(Holder(nullable = null, nonNullable = 50), Json.decodeFromString("""{"nullable":null,"nonNullable":52}""")) + assertEquals(Holder(nullable = 10, nonNullable = 50), Json.decodeFromString("""{"nullable":12,"nonNullable":52}""")) + } + + @Test + fun testFileContextual() { + val module = SerializersModule { + contextual(FileContextualSerializer) + } + + val json = Json { serializersModule = module } + + assertEquals("""{"nullable":null,"nonNullable":"foo"}""", json.encodeToString(FileContextualHolder(null, FileContextualType("foo")))) + assertEquals("""{"nullable":"foo","nonNullable":"bar"}""", json.encodeToString( + FileContextualHolder( + FileContextualType("foo"), FileContextualType("bar") + ) + )) + + assertEquals(FileContextualHolder(null, FileContextualType("foo")), json.decodeFromString("""{"nullable":null,"nonNullable":"foo"}""")) + assertEquals(FileContextualHolder(FileContextualType("foo"), FileContextualType("bar")), json.decodeFromString("""{"nullable":"foo","nonNullable":"bar"}""")) + } + + @Test + fun testContextual() { + val module = SerializersModule { + contextual(ContextualSerializer) + } + + val json = Json { serializersModule = module } + + assertEquals("""{"nullable":null,"nonNullable":"foo"}""", json.encodeToString(ContextualHolder(null, ContextualType("foo")))) + assertEquals("""{"nullable":"foo","nonNullable":"bar"}""", json.encodeToString(ContextualHolder(ContextualType("foo"), ContextualType("bar")))) + + assertEquals(ContextualHolder(null, ContextualType("foo")), json.decodeFromString("""{"nullable":null,"nonNullable":"foo"}""")) + assertEquals(ContextualHolder(ContextualType("foo"), ContextualType("bar")), json.decodeFromString("""{"nullable":"foo","nonNullable":"bar"}""")) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt new file mode 100644 index 00000000..a185ccb7 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization + +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlin.native.concurrent.* + +@Serializable +open class PolyBase(val id: Int) { + override fun hashCode(): Int { + return id + } + + override fun toString(): String { + return "PolyBase(id=$id)" + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + other as PolyBase + if (id != other.id) return false + return true + } + +} + +// TODO sandwwraith moving this class to the corresponding tests breaks runtime in unexpected ways +@Serializable +data class PolyDefault(val json: JsonElement) : PolyBase(-1) + +class PolyDefaultWithId(id: Int) : PolyBase(id) + +@Serializable +data class PolyDerived(val s: String) : PolyBase(1) + +val BaseAndDerivedModule = SerializersModule { + polymorphic(PolyBase::class, PolyBase.serializer()) { + subclass(PolyDerived.serializer()) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/SerializableClasses.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/SerializableClasses.kt new file mode 100644 index 00000000..16fdd2fd --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/SerializableClasses.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization + +@Serializable +data class IntData(val intV: Int) + +@Serializable +data class StringData(val data: String) + +enum class SampleEnum { OptionA, OptionB, OptionC } + +@Serializable +data class Box<T>(val boxed: T) + +@Serializable +sealed class SimpleSealed { + @Serializable + public data class SubSealedA(val s: String) : SimpleSealed() + + @Serializable + public data class SubSealedB(val i: Int) : SimpleSealed() +} + +@Serializable +object SampleObject { + val state: String = "myState" +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/SerializableOnPropertyTypeAndTypealiasTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/SerializableOnPropertyTypeAndTypealiasTest.kt new file mode 100644 index 00000000..7c7133c7 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/SerializableOnPropertyTypeAndTypealiasTest.kt @@ -0,0 +1,93 @@ +package kotlinx.serialization + +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlin.test.* + +@Serializable +data class WithDefault(val s: String) + +@Serializable(SerializerA::class) +data class WithoutDefault(val s: String) + +object SerializerA : KSerializer<WithoutDefault> { + override val descriptor: SerialDescriptor + get() = PrimitiveSerialDescriptor("Bruh", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: WithoutDefault) { + encoder.encodeString(value.s) + } + + override fun deserialize(decoder: Decoder): WithoutDefault { + return WithoutDefault(decoder.decodeString()) + } +} + +object SerializerB : KSerializer<WithoutDefault> { + override val descriptor: SerialDescriptor + get() = PrimitiveSerialDescriptor("Bruh", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: WithoutDefault) { + encoder.encodeString(value.s + "#") + } + + override fun deserialize(decoder: Decoder): WithoutDefault { + return WithoutDefault(decoder.decodeString().removeSuffix("#")) + } +} + +object SerializerC : KSerializer<WithDefault> { + override val descriptor: SerialDescriptor + get() = PrimitiveSerialDescriptor("Bruh", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: WithDefault) { + encoder.encodeString(value.s + "#") + } + + override fun deserialize(decoder: Decoder): WithDefault { + return WithDefault(decoder.decodeString().removeSuffix("#")) + } +} + +typealias WithoutDefaultAlias = @Serializable(SerializerB::class) WithoutDefault +typealias WithDefaultAlias = @Serializable(SerializerC::class) WithDefault + +@Serializable +data class TesterWithoutDefault( + val b1: WithoutDefault, + @Serializable(SerializerB::class) val b2: WithoutDefault, + val b3: @Serializable(SerializerB::class) WithoutDefault, + val b4: WithoutDefaultAlias +) + +@Serializable +data class TesterWithDefault( + val b1: WithDefault, + @Serializable(SerializerC::class) val b2: WithDefault, + val b3: @Serializable(SerializerC::class) WithDefault, + val b4: WithDefaultAlias +) + +class SerializableOnPropertyTypeAndTypealiasTest : JsonTestBase() { + + @Test + fun testWithDefault() { + val t = TesterWithDefault(WithDefault("a"), WithDefault("b"), WithDefault("c"), WithDefault("d")) + assertJsonFormAndRestored( + TesterWithDefault.serializer(), + t, + """{"b1":{"s":"a"},"b2":"b#","b3":"c#","b4":"d#"}""" + ) + } + + @Test + fun testWithoutDefault() { // Ignored by #1895 + val t = TesterWithoutDefault(WithoutDefault("a"), WithoutDefault("b"), WithoutDefault("c"), WithoutDefault("d")) + assertJsonFormAndRestored( + TesterWithoutDefault.serializer(), + t, + """{"b1":"a","b2":"b#","b3":"c#","b4":"d#"}""" + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/SerializationForNullableTypeOnFileTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/SerializationForNullableTypeOnFileTest.kt new file mode 100644 index 00000000..42f2a850 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/SerializationForNullableTypeOnFileTest.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +@file:UseSerializers(NullableIntSerializer::class, NonNullableIntSerializer::class) + +package kotlinx.serialization + +import kotlinx.serialization.SerializationForNullableTypeOnFileTest.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlin.test.* + +class SerializationForNullableTypeOnFileTest { + + @Serializable + data class Holder(val nullable: Int?, val nonNullable: Int) + + object NullableIntSerializer : KSerializer<Int?> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("NullableIntSerializer", PrimitiveKind.INT).nullable + + override fun serialize(encoder: Encoder, value: Int?) { + if (value == null) encoder.encodeNull() + else encoder.encodeInt(value + 1) + } + override fun deserialize(decoder: Decoder): Int? { + return if (decoder.decodeNotNullMark()) { + val value = decoder.decodeInt() + value - 1 + } else { + decoder.decodeNull() + } + } + } + + object NonNullableIntSerializer : KSerializer<Int> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("NotNullIntSerializer", PrimitiveKind.INT) + + override fun serialize(encoder: Encoder, value: Int) { + return encoder.encodeInt(value + 2) + } + + override fun deserialize(decoder: Decoder): Int { + return (decoder.decodeInt() - 2) + } + } + + @Test + fun testFileLevel() { + assertEquals("""{"nullable":null,"nonNullable":52}""", Json.encodeToString(Holder(nullable = null, nonNullable = 50))) + assertEquals("""{"nullable":1,"nonNullable":2}""", Json.encodeToString(Holder(nullable = 0, nonNullable = 0))) + assertEquals("""{"nullable":11,"nonNullable":52}""", Json.encodeToString(Holder(nullable = 10, nonNullable = 50))) + + assertEquals(Holder(nullable = 0, nonNullable = 50), Json.decodeFromString("""{"nullable":1,"nonNullable":52}""")) + assertEquals(Holder(nullable = null, nonNullable = 50), Json.decodeFromString("""{"nullable":null,"nonNullable":52}""")) + assertEquals(Holder(nullable = 10, nonNullable = 50), Json.decodeFromString("""{"nullable":11,"nonNullable":52}""")) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/SerializerForNullableTypeTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/SerializerForNullableTypeTest.kt new file mode 100644 index 00000000..98f3f5e0 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/SerializerForNullableTypeTest.kt @@ -0,0 +1,139 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization + +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlinx.serialization.test.* +import kotlin.test.* + +public class SerializerForNullableTypeTest : JsonTestBase() { + + // Nullable boxes + @Serializable(with = StringHolderSerializer::class) + data class StringHolder(val s: String) + + object StringHolderSerializer : KSerializer<StringHolder?> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("SHS", PrimitiveKind.STRING).nullable + + override fun serialize(encoder: Encoder, value: StringHolder?) { + if (value == null) encoder.encodeString("nullable") + else encoder.encodeString("non-nullable") + } + + override fun deserialize(decoder: Decoder): StringHolder? { + if (decoder.decodeNotNullMark()) { + return StringHolder("non-null: " + decoder.decodeString()) + } + decoder.decodeNull() + return StringHolder("nullable") + } + } + + @Serializable + data class Box(val s: StringHolder?) + + @Test + fun testNullableBoxWithNotNull() { + val b = Box(StringHolder("box")) + val string = Json.encodeToString(b) + assertEquals("""{"s":"non-nullable"}""", string) + val deserialized = Json.decodeFromString<Box>(string) + assertEquals(Box(StringHolder("non-null: non-nullable")), deserialized) + } + + @Test + fun testNullableBoxWithNull() { + val b = Box(null) + val string = Json.encodeToString(b) + assertEquals("""{"s":"nullable"}""", string) + val deserialized = Json.decodeFromString<Box>(string) + assertEquals(Box(StringHolder("non-null: nullable")), deserialized) + } + + @Test + fun testNullableBoxDeserializeNull() { + val deserialized = Json.decodeFromString<Box>("""{"s":null}""") + assertEquals(Box(StringHolder("nullable")), deserialized) + } + + // Nullable primitives + object NullableLongSerializer : KSerializer<Long?> { + + @Serializable + data class OptionalLong(val initialized: Boolean, val value: Long? = 0) + + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("NLS") { + element<Boolean>("initialized") + element<Long?>("value") + }.nullable + + override fun serialize(encoder: Encoder, value: Long?) { + val opt = OptionalLong(value != null, value) + encoder.encodeSerializableValue(OptionalLong.serializer(), opt) + } + + override fun deserialize(decoder: Decoder): Long? { + val value = decoder.decodeSerializableValue(OptionalLong.serializer()) + return if (value.initialized) value.value else null + } + } + + @Serializable + data class NullablePrimitive( + @Serializable(with = NullableLongSerializer::class) val value: Long? + ) + + @Test + fun testNullableLongWithNotNull() { + val data = NullablePrimitive(42) + val json = Json.encodeToString(data) + assertEquals("""{"value":{"initialized":true,"value":42}}""", Json.encodeToString(data)) + assertEquals(data, Json.decodeFromString(json)) + } + + @Test + fun testNullableLongWithNull() { + val data = NullablePrimitive(null) + val json = Json.encodeToString(data) + assertEquals("""{"value":{"initialized":false,"value":null}}""", Json.encodeToString(data)) + assertEquals(data, Json.decodeFromString(json)) + } + + // Now generics + @Serializable + data class GenericNullableBox<T: Any>(val value: T?) + + @Serializable + data class GenericBox<T>(val value: T?) + + @Test + fun testGenericBoxNullable() { + val data = GenericBox<StringHolder?>(null) + val json = Json.encodeToString(data) + assertEquals("""{"value":"nullable"}""", Json.encodeToString(data)) + assertEquals(GenericBox(StringHolder("non-null: nullable")), Json.decodeFromString(json)) + } + + @Test + fun testGenericNullableBoxFromNull() { + assertEquals(GenericBox(StringHolder("nullable")), Json.decodeFromString("""{"value":null}""")) + } + + @Test + fun testGenericNullableBoxNullable() { + val data = GenericNullableBox<StringHolder>(null) + val json = Json.encodeToString(data) + assertEquals("""{"value":"nullable"}""", Json.encodeToString(data)) + assertEquals(GenericNullableBox(StringHolder("non-null: nullable")), Json.decodeFromString(json)) + } + + @Test + fun testGenericBoxNullableFromNull() { + assertEquals(GenericNullableBox(StringHolder("nullable")), Json.decodeFromString("""{"value":null}""")) + } + +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt new file mode 100644 index 00000000..4b4aebfd --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt @@ -0,0 +1,332 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization + +import kotlinx.serialization.builtins.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.features.sealed.SealedChild +import kotlinx.serialization.features.sealed.SealedParent +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlinx.serialization.test.* +import kotlin.reflect.* +import kotlin.test.* +import kotlin.time.Duration + +@Suppress("RemoveExplicitTypeArguments") // This is exactly what's being tested +class SerializersLookupTest : JsonTestBase() { + + @Test + fun testPrimitive() { + val token = typeOf<Int>() + val serial = serializer(token) + assertSame(Int.serializer() as KSerializer<*>, serial) + assertSerializedWithType("42", 42) + } + + @Test + fun testPlainClass() { + val b = StringData("some string") + assertSerializedWithType("""{"data":"some string"}""", b) + } + + @Test + fun testListWithT() { + val source = """[{"intV":42}]""" + val serial = serializer<List<IntData>>() + assertEquals(listOf(IntData(42)), Json.decodeFromString(serial, source)) + } + + @Test + fun testPrimitiveList() { + val myArr = listOf("a", "b", "c") + assertSerializedWithType("""["a","b","c"]""", myArr) + } + + @Test + fun testListAsCollection() { + val myArr: Collection<String> = listOf("a", "b", "c") + assertSerializedWithType("""["a","b","c"]""", myArr) + } + + @Test + fun testUnsigned() { + assertSame(UByte.serializer(), serializer<UByte>()) + assertSame(UShort.serializer(), serializer<UShort>()) + assertSame(UInt.serializer(), serializer<UInt>()) + assertSame(ULong.serializer(), serializer<ULong>()) + } + + @Test + @OptIn(ExperimentalUnsignedTypes::class) + fun testUnsignedArrays() { + assertSame(UByteArraySerializer(), serializer<UByteArray>()) + assertSame(UShortArraySerializer(), serializer<UShortArray>()) + assertSame(UIntArraySerializer(), serializer<UIntArray>()) + assertSame(ULongArraySerializer(), serializer<ULongArray>()) + } + + @Test + fun testPrimitiveSet() { + val mySet = setOf("a", "b", "c", "c") + assertSerializedWithType("""["a","b","c"]""", mySet) + } + + @Test + fun testMapWithT() { + val myMap = mapOf("string" to StringData("foo"), "string2" to StringData("bar")) + assertSerializedWithType("""{"string":{"data":"foo"},"string2":{"data":"bar"}}""", myMap) + } + + @Test + fun testNestedLists() { + val myList = listOf(listOf(listOf(1, 2, 3)), listOf()) + assertSerializedWithType("[[[1,2,3]],[]]", myList) + } + + @Test + fun testListSubtype() { + val myList = arrayListOf(1, 2, 3) + assertSerializedWithType<ArrayList<Int>>("[1,2,3]", myList) + assertSerializedWithType<List<Int>>("[1,2,3]", myList) + } + + @Test + fun testListProjection() { + val myList = arrayListOf(1, 2, 3) + assertSerializedWithType<List<Int>>("[1,2,3]", myList) + assertSerializedWithType<MutableList<out Int>>("[1,2,3]", myList) + assertSerializedWithType<ArrayList<in Int>>("[1,2,3]", myList) + } + + @Test + fun testStarProjectionsAreProhibited() { + val expectedMessage = "Star projections in type arguments are not allowed" + assertFailsWithMessage<IllegalArgumentException>(expectedMessage) { + serializer<Box<*>>() + } + assertFailsWithMessage<IllegalArgumentException>(expectedMessage) { + serializer(typeOf<Box<*>>()) + } + assertFailsWithMessage<IllegalArgumentException>(expectedMessage) { + serializerOrNull(typeOf<Box<*>>()) + } + } + + @Test + fun testNullableTypes() { + val myList: List<Int?> = listOf(1, null, 3) + assertSerializedWithType("[1,null,3]", myList) + assertSerializedWithType<List<Int?>?>("[1,null,3]", myList) + } + + @Test + fun testPair() { + val myPair = "42" to 42 + assertSerializedWithType("""{"first":"42","second":42}""", myPair) + } + + @Test + fun testTriple() { + val myTriple = Triple("1", 2, Box(42)) + assertSerializedWithType("""{"first":"1","second":2,"third":{"boxed":42}}""", myTriple) + } + + @Test + fun testLookupDuration() { + assertNotNull(serializerOrNull(typeOf<Duration>())) + assertSame(Duration.serializer(), serializer<Duration>()) + } + + @Test + fun testCustomGeneric() { + val intBox = Box(42) + val intBoxSerializer = serializer<Box<Int>>() + assertEquals(Box.serializer(Int.serializer()).descriptor, intBoxSerializer.descriptor) + assertSerializedWithType("""{"boxed":42}""", intBox) + val dataBox = Box(StringData("foo")) + assertSerializedWithType("""{"boxed":{"data":"foo"}}""", dataBox) + } + + @Test + fun testRecursiveGeneric() { + val boxBox = Box(Box(Box(IntData(42)))) + assertSerializedWithType("""{"boxed":{"boxed":{"boxed":{"intV":42}}}}""", boxBox) + } + + @Test + fun testMixedGeneric() { + val listOfBoxes = listOf(Box("foo"), Box("bar")) + assertSerializedWithType("""[{"boxed":"foo"},{"boxed":"bar"}]""", listOfBoxes) + val boxedList = Box(listOf("foo", "bar")) + assertSerializedWithType("""{"boxed":["foo","bar"]}""", boxedList) + } + + @Test + fun testReferenceArrays() { + assertSerializedWithType("[1,2,3]", Array<Int>(3) { it + 1 }, default) + assertSerializedWithType("""["1","2","3"]""", Array<String>(3) { (it + 1).toString() }, default) + assertSerializedWithType("[[0],[1],[2]]", Array<Array<Int>>(3) { cnt -> Array(1) { cnt } }, default) + assertSerializedWithType("""[{"boxed":"foo"}]""", Array(1) { Box("foo") }, default) + assertSerializedWithType("""[[{"boxed":"foo"}]]""", Array(1) { Array(1) { Box("foo") } }, default) + } + + @Test + fun testPrimitiveArrays() { + assertSerializedWithType("[1,2,3]", intArrayOf(1, 2, 3), default) + assertSerializedWithType("[1,2,3]", longArrayOf(1, 2, 3), default) + assertSerializedWithType("[1,2,3]", byteArrayOf(1, 2, 3), default) + assertSerializedWithType("[1,2,3]", shortArrayOf(1, 2, 3), default) + assertSerializedWithType("[true,false]", booleanArrayOf(true, false), default) + assertSerializedWithType("""["a","b","c"]""", charArrayOf('a', 'b', 'c'), default) + } + + @Test + fun testSerializableObject() { + assertSerializedWithType("{}", SampleObject) + } + + class IntBox(val i: Int) + + class CustomIntSerializer(isNullable: Boolean) : KSerializer<IntBox?> { + override val descriptor: SerialDescriptor + + init { + val d = PrimitiveSerialDescriptor("CIS", PrimitiveKind.INT) + descriptor = if (isNullable) d.nullable else d + } + + override fun serialize(encoder: Encoder, value: IntBox?) { + if (value == null) encoder.encodeInt(41) + else encoder.encodeInt(42) + } + + override fun deserialize(decoder: Decoder): IntBox? { + TODO() + } + } + + class GenericHolder<T>(value: T) + + class GenericSerializer<T>(typeSerializer: KSerializer<T>) : KSerializer<GenericHolder<T>> { + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor( + "Generic Serializer parametrized by ${typeSerializer.descriptor}", + PrimitiveKind.STRING + ) + + override fun deserialize(decoder: Decoder): GenericHolder<T> { + TODO() + } + + override fun serialize(encoder: Encoder, value: GenericHolder<T>) { + TODO() + } + } + + @Test + fun testContextualLookup() { + val module = SerializersModule { contextual(CustomIntSerializer(false).cast<IntBox>()) } + val json = Json { serializersModule = module } + val data = listOf(listOf(IntBox(1))) + assertEquals("[[42]]", json.encodeToString(data)) + } + + @Test + fun testGenericOfContextual() { + val module = SerializersModule { + contextual(CustomIntSerializer(false).cast<IntBox>()) + contextual(GenericHolder::class) { args -> GenericSerializer(args[0]) } + } + + val listSerializer = module.serializerOrNull(typeOf<List<IntBox>>()) + assertNotNull(listSerializer) + assertEquals("kotlin.collections.ArrayList(PrimitiveDescriptor(CIS))", listSerializer.descriptor.toString()) + + val genericSerializer = module.serializerOrNull(typeOf<GenericHolder<IntBox>>()) + assertNotNull(genericSerializer) + assertEquals( + "PrimitiveDescriptor(Generic Serializer parametrized by PrimitiveDescriptor(CIS))", + genericSerializer.descriptor.toString() + ) + } + + @Test + fun testContextualLookupNullable() { + val module = SerializersModule { contextual(CustomIntSerializer(true).cast<IntBox>()) } + val serializer = module.serializer<List<List<IntBox?>>>() + assertEquals("[[41]]", Json.encodeToString(serializer, listOf(listOf<IntBox?>(null)))) + } + + @Test + fun testContextualLookupNonNullable() { + val module = SerializersModule { contextual(CustomIntSerializer(false).cast<IntBox>()) } + val serializer = module.serializer<List<List<IntBox?>>>() + assertEquals("[[null]]", Json.encodeToString(serializer, listOf(listOf<IntBox?>(null)))) + } + + @Test + fun testCompiledWinsOverContextual() { + val contextual = object : KSerializer<Int> { + override val descriptor: SerialDescriptor = Int.serializer().descriptor + + override fun serialize(encoder: Encoder, value: Int) { + fail() + } + + override fun deserialize(decoder: Decoder): Int { + fail() + } + } + val json = Json { serializersModule = SerializersModule { contextual(contextual) } } + assertEquals("[[1]]", json.encodeToString(listOf(listOf<Int>(1)))) + assertEquals("42", json.encodeToString(42)) + } + + class NonSerializable + + class NonSerializableBox<T>(val boxed: T) + + @Test + fun testSealedFromOtherFileLookup() { + assertNotNull(serializerOrNull(typeOf<SealedParent>())) + assertNotNull(serializerOrNull(typeOf<SealedChild>())) + } + + @Test + fun testLookupFail() { + assertNull(serializerOrNull(typeOf<NonSerializable>())) + assertNull(serializerOrNull(typeOf<NonSerializableBox<String>>())) + assertNull(serializerOrNull(typeOf<Box<NonSerializable>>())) + + assertFailsWithMessage<SerializationException>("for class 'NonSerializable'") { + serializer(typeOf<NonSerializable>()) + } + + assertFailsWithMessage<SerializationException>("for class 'NonSerializableBox'") { + serializer(typeOf<NonSerializableBox<String>>()) + } + + assertFailsWithMessage<SerializationException>("for class 'NonSerializable'") { + serializer(typeOf<Box<NonSerializable>>()) + } + } + + private inline fun <reified T> assertSerializedWithType( + expected: String, + value: T, + json: StringFormat = default + ) { + val serial = serializer<T>() + assertEquals(expected, json.encodeToString(serial, value)) + val serial2 = requireNotNull(serializerOrNull(typeOf<T>())) { "Expected serializer to be found" } + assertEquals(expected, json.encodeToString(serial2, value)) + } + + @Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") + inline fun <T> KSerializer<*>.cast(): KSerializer<T> = this as KSerializer<T> + +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/TuplesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/TuplesTest.kt new file mode 100644 index 00000000..9fd2d5ea --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/TuplesTest.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization + +import kotlinx.serialization.builtins.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.json.* +import kotlinx.serialization.test.assertStringFormAndRestored +import kotlin.test.* + +class TuplesTest : JsonTestBase() { + @Serializable + data class MyPair<K, V>(val k: K, val v: V) + + @Serializable + data class PairWrapper(val p: Pair<Int, String>) + + @Serializable + data class TripleWrapper(val t: Triple<Int, String, Boolean>) + + @Test + fun testCustomPair() = assertStringFormAndRestored( + """{"k":42,"v":"foo"}""", + MyPair(42, "foo"), + MyPair.serializer( + Int.serializer(), + String.serializer() + ), + lenient + ) + + @Test + fun testStandardPair() = assertStringFormAndRestored( + """{"p":{"first":42,"second":"foo"}}""", + PairWrapper(42 to "foo"), + PairWrapper.serializer(), + lenient + ) + + @Test + fun testStandardPairHasCorrectDescriptor() { + val desc = PairWrapper.serializer().descriptor.getElementDescriptor(0) + assertEquals(desc.serialName, "kotlin.Pair") + assertEquals( + desc.elementDescriptors.map(SerialDescriptor::kind), + listOf(PrimitiveKind.INT, PrimitiveKind.STRING) + ) + } + + @Test + fun testStandardTriple() = assertStringFormAndRestored( + """{"t":{"first":42,"second":"foo","third":false}}""", + TripleWrapper(Triple(42, "foo", false)), + TripleWrapper.serializer(), + lenient + ) + + @Test + fun testStandardTripleHasCorrectDescriptor() { + val desc = TripleWrapper.serializer().descriptor.getElementDescriptor(0) + assertEquals(desc.serialName, "kotlin.Triple") + assertEquals( + desc.elementDescriptors.map(SerialDescriptor::kind), + listOf(PrimitiveKind.INT, PrimitiveKind.STRING, PrimitiveKind.BOOLEAN) + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/UmbrellaTypes.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/UmbrellaTypes.kt new file mode 100644 index 00000000..f878c633 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/UmbrellaTypes.kt @@ -0,0 +1,107 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization + +import kotlin.native.concurrent.* + +enum class Attitude { POSITIVE, NEUTRAL, NEGATIVE } + +@Serializable +data class Tree(val name: String, val left: Tree? = null, val right: Tree? = null) + +@Serializable +data class TypesUmbrella( + val unit: Unit, + val boolean: Boolean, + val byte: Byte, + val short: Short, + val int: Int, + val long: Long, + val float: Float, + val double: Double, + val char: Char, + val string: String, + val enum: Attitude, + val intData: IntData, + val unitN: Unit?, + val booleanN: Boolean?, + val byteN: Byte?, + val shortN: Short?, + val intN: Int?, + val longN: Long?, + val floatN: Float?, + val doubleN: Double?, + val charN: Char?, + val stringN: String?, + val enumN: Attitude?, + val intDataN: IntData?, + val listInt: List<Int>, + val listIntN: List<Int?>, + val listNInt: Set<Int>?, + val listNIntN: MutableSet<Int?>?, + val listListEnumN: List<List<Attitude?>>, + val listIntData: List<IntData>, + val listIntDataN: MutableList<IntData?>, + val tree: Tree, + val mapStringInt: Map<String, Int>, + val mapIntStringN: Map<Int, String?>, + val arrays: ArraysUmbrella +) + +@Serializable +data class ArraysUmbrella( + val arrByte: Array<Byte>, + val arrInt: Array<Int>, + val arrIntN: Array<Int?>, + val arrIntData: Array<IntData> +) { + override fun equals(other: Any?) = other is ArraysUmbrella && + arrByte.contentEquals(other.arrByte) && + arrInt.contentEquals(other.arrInt) && + arrIntN.contentEquals(other.arrIntN) && + arrIntData.contentEquals(other.arrIntData) +} + +val umbrellaInstance = TypesUmbrella( + Unit, true, 10, 20, 30, 40, 50.1f, 60.1, 'A', "Str0", Attitude.POSITIVE, IntData(70), + null, null, 11, 21, 31, 41, 51.1f, 61.1, 'B', "Str1", Attitude.NEUTRAL, null, + listOf(1, 2, 3), + listOf(4, 5, null), + setOf(6, 7, 8), + mutableSetOf(null, 9, 10), + listOf(listOf(Attitude.NEGATIVE, null)), + listOf(IntData(1), IntData(2), IntData(3)), + mutableListOf(IntData(1), null, IntData(3)), + Tree("root", Tree("left"), Tree("right", Tree("right.left"), Tree("right.right"))), + mapOf("one" to 1, "two" to 2, "three" to 3), + mapOf(0 to null, 1 to "first", 2 to "second"), + ArraysUmbrella( + arrayOf(1, 2, 3), + arrayOf(100, 200, 300), + arrayOf(null, -1, -2), + arrayOf(IntData(1), IntData(2)) + ) +) + +val umbrellaInstance2 = TypesUmbrella( + Unit, true, 10, 20, 30, 40, 50.5f, 60.5, 'A', "Str0", Attitude.POSITIVE, IntData(70), + null, null, 11, 21, 31, 41, 51.5f, 61.5, 'B', "Str1", Attitude.NEUTRAL, null, + listOf(1, 2, 3), + listOf(4, 5, null), + setOf(6, 7, 8), + mutableSetOf(null, 9, 10), + listOf(listOf(Attitude.NEGATIVE, null)), + listOf(IntData(1), IntData(2), IntData(3)), + mutableListOf(IntData(1), null, IntData(3)), + Tree("root", Tree("left"), Tree("right", Tree("right.left"), Tree("right.right"))), + mapOf("one" to 1, "two" to 2, "three" to 3), + mapOf(0 to null, 1 to "first", 2 to "second"), + ArraysUmbrella( + arrayOf(1, 2, 3), + arrayOf(100, 200, 300), + arrayOf(null, -1, -2), + arrayOf(IntData(1), IntData(2)) + ) +) diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/UnknownElementIndexTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/UnknownElementIndexTest.kt new file mode 100644 index 00000000..3fb4bc04 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/UnknownElementIndexTest.kt @@ -0,0 +1,39 @@ +package kotlinx.serialization + +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.CompositeDecoder.Companion.UNKNOWN_NAME +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.* +import kotlin.test.Test +import kotlin.test.assertFailsWith + +class UnknownElementIndexTest { + enum class Choices { A, B, C } + + @Serializable + data class Holder(val c: Choices) + + class MalformedReader : AbstractDecoder() { + override val serializersModule: SerializersModule = EmptySerializersModule() + + override fun decodeElementIndex(descriptor: SerialDescriptor): Int { + return UNKNOWN_NAME + } + } + + @Test + fun testCompilerComplainsAboutIncorrectIndex() { + assertFailsWith(SerializationException::class) { + MalformedReader().decodeSerializableValue(Holder.serializer()) + } + } + + @Test + fun testErrorMessage() { + val message = "kotlinx.serialization.UnknownElementIndexTest.Choices does not contain element with name 'D'" + assertFailsWith(SerializationException::class, message) { + Json.decodeFromString(Holder.serializer(), """{"c":"D"}""") + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/builtins/KeyValueSerializersTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/builtins/KeyValueSerializersTest.kt new file mode 100644 index 00000000..c6a1bbe3 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/builtins/KeyValueSerializersTest.kt @@ -0,0 +1,142 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.builtins + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class KeyValueSerializersTest : JsonTestBase() { + + @Test + fun testPair() = parametrizedTest { jsonTestingMode -> + testPair(Pair(42, 42), Int.serializer(), Int.serializer(), jsonTestingMode, """{"first":42,"second":42}""") + testPair( + Pair(42, Pair("a", "b")), + Int.serializer(), + serializer(), + jsonTestingMode, + """{"first":42,"second":{"first":"a","second":"b"}}""" + ) + testPair( + Pair(42, null), + Int.serializer(), + Int.serializer().nullable, + jsonTestingMode, + """{"first":42,"second":null}""" + ) + } + + private fun <K, V> testPair( + pairInstance: Pair<K, V>, + kSerializer: KSerializer<K>, + vSerializer: KSerializer<V>, + jsonTestingMode: JsonTestingMode, + expectedJson: String + ) { + val serializer = PairSerializer(kSerializer, vSerializer) + val json = default.encodeToString(serializer, pairInstance, jsonTestingMode) + assertEquals(expectedJson, json) + val pair = default.decodeFromString(serializer, json, jsonTestingMode) + assertEquals(pairInstance, pair) + } + + @Test + fun testTriple() = parametrizedTest { jsonTestingMode -> + testTriple( + Triple(42, 42, "42"), + Int.serializer(), + Int.serializer(), + String.serializer(), + jsonTestingMode, + """{"first":42,"second":42,"third":"42"}""" + ) + + testTriple( + Triple(42, Triple(42, "f", 'c'), "42"), + Int.serializer(), + serializer(), + String.serializer(), + jsonTestingMode, + """{"first":42,"second":{"first":42,"second":"f","third":"c"},"third":"42"}""" + ) + + testTriple( + Triple(42, null, null), + Int.serializer(), + Int.serializer().nullable, + String.serializer().nullable, + jsonTestingMode, + """{"first":42,"second":null,"third":null}""" + ) + } + + private fun <A, B, C> testTriple( + tripleInstance: Triple<A, B, C>, + aSerializer: KSerializer<A>, + bSerializer: KSerializer<B>, + cSerializer: KSerializer<C>, + jsonTestingMode: JsonTestingMode, + expectedJson: String + ) { + val serializer = TripleSerializer(aSerializer, bSerializer, cSerializer) + val json = default.encodeToString(serializer, tripleInstance, jsonTestingMode) + assertEquals(expectedJson, json) + val triple = default.decodeFromString(serializer, json, jsonTestingMode) + assertEquals(tripleInstance, triple) + } + + class Entry<K, V>(override val key: K, override val value: V) : Map.Entry<K, V> { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other !is Map.Entry<*, *>) return false + if (key != other.key) return false + if (value != other.value) return false + return true + } + + override fun hashCode(): Int { + var result = key?.hashCode() ?: 0 + result = 31 * result + (value?.hashCode() ?: 0) + return result + } + } + + @Test + fun testKeyValuePair() = parametrizedTest { jsonTestingMode -> + jvmOnly { + testEntry(Entry(42, 42), Int.serializer(), Int.serializer(), jsonTestingMode, """{"42":42}""") + testEntry( + Entry(42, Entry("a", "b")), + Int.serializer(), + serializer<Map.Entry<String, String>>(), + jsonTestingMode, + """{"42":{"a":"b"}}""" + ) + testEntry( + Entry(42, null), + Int.serializer(), + Int.serializer().nullable, + jsonTestingMode, + """{"42":null}""" + ) + } + } + + private inline fun <reified K, reified V> testEntry( + entryInstance: Map.Entry<K, V>, + kSerializer: KSerializer<K>, + vSerializer: KSerializer<V>, + jsonTestingMode: JsonTestingMode, + expectedJson: String + ) { + val serializer = MapEntrySerializer(kSerializer, vSerializer) + val json = default.encodeToString(serializer, entryInstance, jsonTestingMode) + assertEquals(expectedJson, json) + val entry = default.decodeFromString(serializer, json, jsonTestingMode) + assertEquals(entryInstance, entry) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/BinaryPayloadExampleTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/BinaryPayloadExampleTest.kt new file mode 100644 index 00000000..047f1a36 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/BinaryPayloadExampleTest.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2017-2020 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.builtins.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.test.* +import kotlin.test.Test +import kotlin.test.assertEquals + +class BinaryPayloadExampleTest { + @Serializable(BinaryPayload.Companion::class) + class BinaryPayload(val req: ByteArray, val res: ByteArray) { + companion object : KSerializer<BinaryPayload> { + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("BinaryPayload") { + element("req", ByteArraySerializer().descriptor) + element("res", ByteArraySerializer().descriptor) + } + + override fun serialize(encoder: Encoder, value: BinaryPayload) { + val compositeOutput = encoder.beginStructure(descriptor) + compositeOutput.encodeStringElement(descriptor, 0, InternalHexConverter.printHexBinary(value.req)) + compositeOutput.encodeStringElement(descriptor, 1, InternalHexConverter.printHexBinary(value.res)) + compositeOutput.endStructure(descriptor) + } + + override fun deserialize(decoder: Decoder): BinaryPayload { + val dec: CompositeDecoder = decoder.beginStructure(descriptor) + var req: ByteArray? = null // consider using flags or bit mask if you + var res: ByteArray? = null // need to read nullable non-optional properties + loop@ while (true) { + when (val i = dec.decodeElementIndex(descriptor)) { + CompositeDecoder.DECODE_DONE -> break@loop + 0 -> req = InternalHexConverter.parseHexBinary(dec.decodeStringElement(descriptor, i)) + 1 -> res = InternalHexConverter.parseHexBinary(dec.decodeStringElement(descriptor, i)) + else -> throw SerializationException("Unknown index $i") + } + } + dec.endStructure(descriptor) + return BinaryPayload( + req ?: throw SerializationException("MFE: req"), + res ?: throw SerializationException("MFE: res") + ) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as BinaryPayload + + if (!req.contentEquals(other.req)) return false + if (!res.contentEquals(other.res)) return false + + return true + } + + override fun hashCode(): Int { + var result = req.contentHashCode() + result = 31 * result + res.contentHashCode() + return result + } + } + + @Test + fun payloadEquivalence() { + val payload1 = BinaryPayload(byteArrayOf(0, 0, 0), byteArrayOf(127, 127)) + val s = Json.encodeToString(BinaryPayload.serializer(), payload1) + assertEquals("""{"req":"000000","res":"7F7F"}""", s) + val payload2 = Json.decodeFromString(BinaryPayload.serializer(), s) + assertEquals(payload1, payload2) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/ByteArraySerializerTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/ByteArraySerializerTest.kt new file mode 100644 index 00000000..aa1ad2d0 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/ByteArraySerializerTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2017-2019 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.builtins.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.test.* +import kotlin.test.* + +class ByteArraySerializerTest { + + @Serializable + class ByteArrayCarrier(@Id(2) val data: ByteArray) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as ByteArrayCarrier + + if (!data.contentEquals(other.data)) return false + + return true + } + + override fun hashCode(): Int { + return data.contentHashCode() + } + + override fun toString(): String { + return "ByteArrayCarrier(data=${data.contentToString()})" + } + } + + @Test + fun testByteArrayJson() { + val bytes = byteArrayOf(42, 43, 44, 45) + val s = Json.encodeToString(ByteArraySerializer(), bytes) + assertEquals(s, """[42,43,44,45]""") + val bytes2 = Json.decodeFromString(ByteArraySerializer(), s) + assertTrue(bytes.contentEquals(bytes2)) + } + + @Test + fun testWrappedByteArrayJson() { + val obj = ByteArrayCarrier(byteArrayOf(42, 100)) + val s = Json.encodeToString(ByteArrayCarrier.serializer(), obj) + assertEquals("""{"data":[42,100]}""", s) + val obj2 = Json.decodeFromString(ByteArrayCarrier.serializer(), s) + assertEquals(obj, obj2) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/CollectionSerializerTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/CollectionSerializerTest.kt new file mode 100644 index 00000000..022ef0eb --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/CollectionSerializerTest.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2017-2022 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.Json +import kotlin.test.* + +class CollectionSerializerTest { + + @Serializable + data class CollectionWrapper( + val collection: Collection<String> + ) + + @Test + fun testListJson() { + val list = listOf("foo", "bar", "foo", "bar") + + val string = Json.encodeToString(CollectionWrapper(list)) + assertEquals("""{"collection":["foo","bar","foo","bar"]}""", string) + + val wrapper = Json.decodeFromString<CollectionWrapper>(string) + assertEquals(list, wrapper.collection) + } + + @Test + fun testSetJson() { + val set = setOf("foo", "bar", "foo", "bar") + + val string = Json.encodeToString(CollectionWrapper(set)) + assertEquals("""{"collection":["foo","bar"]}""", string) + + val wrapper = Json.decodeFromString<CollectionWrapper>(string) + assertEquals(set.toList(), wrapper.collection) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/ContextAndPolymorphicTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/ContextAndPolymorphicTest.kt new file mode 100644 index 00000000..ac24cf04 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/ContextAndPolymorphicTest.kt @@ -0,0 +1,137 @@ +/* + * Copyright 2017-2020 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.builtins.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class ContextAndPolymorphicTest { + + @Serializable + data class Data(val a: Int, val b: Int = 42) + + @Serializable + data class EnhancedData( + val data: Data, + @Contextual val stringPayload: Payload, + @Serializable(with = BinaryPayloadSerializer::class) val binaryPayload: Payload + ) + + @Serializable + @SerialName("Payload") + data class Payload(val s: String) + + @Serializable + data class PayloadList(val ps: List<@Contextual Payload>) + + @Serializer(forClass = Payload::class) + object PayloadSerializer + + object BinaryPayloadSerializer : KSerializer<Payload> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("BinaryPayload", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: Payload) { + encoder.encodeString(InternalHexConverter.printHexBinary(value.s.encodeToByteArray())) + } + + override fun deserialize(decoder: Decoder): Payload { + return Payload(InternalHexConverter.parseHexBinary(decoder.decodeString()).decodeToString()) + } + } + + private val value = EnhancedData(Data(100500), Payload("string"), Payload("binary")) + private lateinit var json: Json + + @BeforeTest + fun initContext() { + val scope = serializersModuleOf(Payload::class, PayloadSerializer) + val bPolymorphicModule = SerializersModule { polymorphic(Any::class) { subclass(PayloadSerializer) } } + json = Json { + useArrayPolymorphism = true + encodeDefaults = true + serializersModule = scope + bPolymorphicModule + } + } + + @Test + fun testWriteCustom() { + val s = json.encodeToString(EnhancedData.serializer(), value) + assertEquals("""{"data":{"a":100500,"b":42},"stringPayload":{"s":"string"},"binaryPayload":"62696E617279"}""", s) + } + + @Test + fun testReadCustom() { + val s = json.decodeFromString( + EnhancedData.serializer(), + """{"data":{"a":100500,"b":42},"stringPayload":{"s":"string"},"binaryPayload":"62696E617279"}""") + assertEquals(value, s) + } + + @Test + fun testWriteCustomList() { + val s = json.encodeToString(PayloadList.serializer(), PayloadList(listOf(Payload("1"), Payload("2")))) + assertEquals("""{"ps":[{"s":"1"},{"s":"2"}]}""", s) + } + + @Test + fun testPolymorphicResolve() { + val map = mapOf<String, Any>("Payload" to Payload("data")) + val serializer = MapSerializer(String.serializer(), PolymorphicSerializer(Any::class)) + val s = json.encodeToString(serializer, map) + assertEquals("""{"Payload":["Payload",{"s":"data"}]}""", s) + } + + @Test + fun testDifferentRepresentations() { + val simpleModule = serializersModuleOf(PayloadSerializer) + val binaryModule = serializersModuleOf(BinaryPayloadSerializer) + + val json1 = Json { useArrayPolymorphism = true; serializersModule = simpleModule } + val json2 = Json { useArrayPolymorphism = true; serializersModule = binaryModule } + + // in json1, Payload would be serialized with PayloadSerializer, + // in json2, Payload would be serialized with BinaryPayloadSerializer + + val list = PayloadList(listOf(Payload("string"))) + assertEquals("""{"ps":[{"s":"string"}]}""", json1.encodeToString(PayloadList.serializer(), list)) + assertEquals("""{"ps":["737472696E67"]}""", json2.encodeToString(PayloadList.serializer(), list)) + } + + private fun SerialDescriptor.inContext(module: SerializersModule): SerialDescriptor = when (kind) { + SerialKind.CONTEXTUAL -> requireNotNull(module.getContextualDescriptor(this)) { "Expected $this to be registered in module" } + else -> error("Expected this function to be called on CONTEXTUAL descriptor") + } + + @Test + fun testResolveContextualDescriptor() { + val simpleModule = serializersModuleOf(PayloadSerializer) + val binaryModule = serializersModuleOf(BinaryPayloadSerializer) + + val contextDesc = EnhancedData.serializer().descriptor.elementDescriptors.toList()[1] // @ContextualSer stringPayload + assertEquals(SerialKind.CONTEXTUAL, contextDesc.kind) + assertEquals(0, contextDesc.elementsCount) + + val resolvedToDefault = contextDesc.inContext(simpleModule) + assertEquals(StructureKind.CLASS, resolvedToDefault.kind) + assertEquals("Payload", resolvedToDefault.serialName) + assertEquals(1, resolvedToDefault.elementsCount) + + val resolvedToBinary = contextDesc.inContext(binaryModule) + assertEquals(PrimitiveKind.STRING, resolvedToBinary.kind) + assertEquals("BinaryPayload", resolvedToBinary.serialName) + } + + @Test + fun testContextualSerializerUsesDefaultIfModuleIsEmpty() { + val s = Json { useArrayPolymorphism = true; encodeDefaults = true }.encodeToString(EnhancedData.serializer(), value) + assertEquals("""{"data":{"a":100500,"b":42},"stringPayload":{"s":"string"},"binaryPayload":"62696E617279"}""", s) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/DefaultPolymorphicSerializerTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/DefaultPolymorphicSerializerTest.kt new file mode 100644 index 00000000..9d35a290 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/DefaultPolymorphicSerializerTest.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2017-2022 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 kotlinx.serialization.modules.* +import kotlin.test.* + +class DefaultPolymorphicSerializerTest : JsonTestBase() { + + @Serializable + abstract class Project { + abstract val name: String + } + + @Serializable + data class DefaultProject(override val name: String, val type: String): Project() + + val module = SerializersModule { + polymorphic(Project::class) { + defaultDeserializer { DefaultProject.serializer() } + } + } + + private val json = Json { serializersModule = module } + + @Test + fun test() = parametrizedTest { + assertEquals( + DefaultProject("example", "unknown"), + json.decodeFromString<Project>(""" {"type":"unknown","name":"example"}""", it)) + } + +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/DerivedContextualSerializerTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/DerivedContextualSerializerTest.kt new file mode 100644 index 00000000..7da16fb8 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/DerivedContextualSerializerTest.kt @@ -0,0 +1,49 @@ +package kotlinx.serialization.features + +import kotlin.test.* +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* + +class DerivedContextualSerializerTest { + + @Serializable + abstract class Message + + @Serializable + class SimpleMessage(val body: String) : Message() + + @Serializable + class Holder(@Contextual val message: Message) + + object MessageAsStringSerializer : KSerializer<Message> { + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("kotlinx.serialization.MessageAsStringSerializer", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: Message) { + // dummy serializer that assumes Message is always SimpleMessage + check(value is SimpleMessage) + encoder.encodeString(value.body) + } + + override fun deserialize(decoder: Decoder): Message { + return SimpleMessage(decoder.decodeString()) + } + } + + @Test + fun testDerivedContextualSerializer() { + val module = SerializersModule { + contextual(MessageAsStringSerializer) + } + val format = Json { serializersModule = module } + val data = Holder(SimpleMessage("hello")) + val serialized = format.encodeToString(data) + assertEquals("""{"message":"hello"}""", serialized) + val deserialized = format.decodeFromString<Holder>(serialized) + assertTrue(deserialized.message is SimpleMessage) + assertEquals("hello", deserialized.message.body) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/DurationTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/DurationTest.kt new file mode 100644 index 00000000..0dbb3f9e --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/DurationTest.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.features + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonTestBase +import kotlin.test.Test +import kotlin.time.Duration +import kotlin.time.DurationUnit +import kotlin.time.toDuration + +class DurationTest : JsonTestBase() { + @Serializable + data class DurationHolder(val duration: Duration) + @Test + fun testDuration() { + assertJsonFormAndRestored( + DurationHolder.serializer(), + DurationHolder(1000.toDuration(DurationUnit.SECONDS)), + """{"duration":"PT16M40S"}""" + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/EmojiTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/EmojiTest.kt new file mode 100644 index 00000000..1e3904ab --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/EmojiTest.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.features + +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.json.JsonTestBase +import kotlin.test.Test + + +class EmojiTest : JsonTestBase() { + + @Test + fun testEmojiString() { + assertJsonFormAndRestored( + String.serializer(), + "\uD83C\uDF34", + "\"\uD83C\uDF34\"" + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/GenericCustomSerializerTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/GenericCustomSerializerTest.kt new file mode 100644 index 00000000..9c623912 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/GenericCustomSerializerTest.kt @@ -0,0 +1,167 @@ +/* + * Copyright 2017-2021 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.builtins.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class CheckedData<T : Any>(val data: T, val checkSum: ByteArray) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as CheckedData<*> + + if (data != other.data) return false + if (!checkSum.contentEquals(other.checkSum)) return false + + return true + } + + override fun hashCode(): Int { + var result = data.hashCode() + result = 31 * result + checkSum.contentHashCode() + return result + } +} + +class CheckedDataSerializer<T : Any>(private val dataSerializer: KSerializer<T>) : KSerializer<CheckedData<T>> { + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("CheckedDataSerializer") { + val dataDescriptor = dataSerializer.descriptor + element("data", dataDescriptor) + element("checkSum", ByteArraySerializer().descriptor) + } + + override fun serialize(encoder: Encoder, value: CheckedData<T>) { + val out = encoder.beginStructure(descriptor) + out.encodeSerializableElement(descriptor, 0, dataSerializer, value.data) + out.encodeStringElement(descriptor, 1, InternalHexConverter.printHexBinary(value.checkSum)) + out.endStructure(descriptor) + } + + override fun deserialize(decoder: Decoder): CheckedData<T> { + val inp = decoder.beginStructure(descriptor) + lateinit var data: T + lateinit var sum: ByteArray + loop@ while (true) { + when (val i = inp.decodeElementIndex(descriptor)) { + CompositeDecoder.DECODE_DONE -> break@loop + 0 -> data = inp.decodeSerializableElement(descriptor, i, dataSerializer) + 1 -> sum = InternalHexConverter.parseHexBinary(inp.decodeStringElement(descriptor, i)) + else -> throw SerializationException("Unknown index $i") + } + } + inp.endStructure(descriptor) + return CheckedData(data, sum) + } +} + +@Serializable +data class DataWithString(@Serializable(with = CheckedDataSerializer::class) val data: CheckedData<String>) + +@Serializable +data class DataWithInt(@Serializable(with = CheckedDataSerializer::class) val data: CheckedData<Int>) + +@Serializable +data class DataWithStringContext(@Contextual val data: CheckedData<String>) + + +@Serializable +data class OptionalHolder(val optionalInt: Optional<Int>) + +@Serializable(OptionalSerializer::class) +sealed class Optional<out T : Any?> { + object NotPresent : Optional<Nothing>() + data class Value<T : Any?>(val value: T?) : Optional<T>() + + fun get(): T? { + return when (this) { + NotPresent -> null + is Value -> this.value + } + } +} + +class OptionalSerializer<T>( + private val valueSerializer: KSerializer<T> +) : KSerializer<Optional<T>> { + override val descriptor: SerialDescriptor = valueSerializer.descriptor + + override fun deserialize(decoder: Decoder): Optional<T> { + return try { + Optional.Value(valueSerializer.deserialize(decoder)) + } catch (exception: Exception) { + Optional.NotPresent + } + } + + override fun serialize(encoder: Encoder, value: Optional<T>) { + val msg = "Tried to serialize an optional property that had no value present. Is encodeDefaults false?" + when (value) { + Optional.NotPresent -> throw SerializationException(msg) + is Optional.Value -> + when (val optional = value.value) { + null -> encoder.encodeNull() + else -> valueSerializer.serialize(encoder, optional) + } + } + } +} + + +class GenericCustomSerializerTest { + @Test + fun testStringData() { + val original = DataWithString(CheckedData("my data", byteArrayOf(42, 32))) + val s = Json.encodeToString(DataWithString.serializer(), original) + assertEquals("""{"data":{"data":"my data","checkSum":"2A20"}}""", s) + val restored = Json.decodeFromString(DataWithString.serializer(), s) + assertEquals(original, restored) + } + + @Test + fun testIntData() { + val original = DataWithInt(CheckedData(42, byteArrayOf(42))) + val s = Json.encodeToString(DataWithInt.serializer(), original) + assertEquals("""{"data":{"data":42,"checkSum":"2A"}}""", s) + val restored = Json.decodeFromString(DataWithInt.serializer(), s) + assertEquals(original, restored) + } + + + @Test + fun testContextualGeneric() { + val module = SerializersModule { + @Suppress("UNCHECKED_CAST") + contextual(CheckedData::class) { args -> CheckedDataSerializer(args[0] as KSerializer<Any>) } + } + assertStringFormAndRestored( + """{"data":{"data":"my data","checkSum":"2A20"}}""", + DataWithStringContext(CheckedData("my data", byteArrayOf(42, 32))), + DataWithStringContext.serializer(), + Json { serializersModule = module } + ) + } + + @Test + fun testOnSealedClass() { + /* + Test on custom serializer for sealed class with generic parameter. + Related issues: + https://github.com/Kotlin/kotlinx.serialization/issues/1705 + https://youtrack.jetbrains.com/issue/KT-50764 + https://youtrack.jetbrains.com/issue/KT-50718 + https://github.com/Kotlin/kotlinx.serialization/issues/1843 + */ + val encoded = Json.encodeToString(OptionalHolder(Optional.Value(42))) + assertEquals("""{"optionalInt":42}""", encoded) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/InheritanceTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/InheritanceTest.kt new file mode 100644 index 00000000..ea11f9b3 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/InheritanceTest.kt @@ -0,0 +1,115 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:Suppress("EqualsOrHashCode") + +package kotlinx.serialization.features + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.test.* +import kotlin.test.* + +@Serializable +abstract class AbstractSerializable { + public abstract val rootState: String // no backing field + + val publicState: String = "A" +} + +@Serializable +open class SerializableBase: AbstractSerializable() { + + + private val privateState: String = "B" // still should be serialized + + @Transient + private val privateTransientState = "C" // not serialized: explicitly transient + + val notAState: String // not serialized: no backing field + get() = "D" + + override val rootState: String + get() = "E" // still not serializable + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is SerializableBase) return false + + if (privateState != other.privateState) return false + if (privateTransientState != other.privateTransientState) return false + + return true + } +} + +@Serializable +class Derived(val derivedState: Int): SerializableBase() { + override val rootState: String = "foo" // serializable! + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Derived) return false + if (!super.equals(other)) return false + + if (derivedState != other.derivedState) return false + if (rootState != other.rootState) return false + + return true + } +} + +@Serializable +open class Base1(open var state1: String) { + override fun toString(): String { + return "Base1(state1='$state1')" + } +} + +@Serializable +class Derived2(@SerialName("state2") override var state1: String): Base1(state1) { + override fun toString(): String { + return "Derived2(state1='$state1')" + } +} + +class InheritanceTest { + private val json = Json { encodeDefaults = true } + + @Test + fun canBeSerializedAsDerived() { + val derived = Derived(42) + val msg = json.encodeToString(Derived.serializer(), derived) + assertEquals("""{"publicState":"A","privateState":"B","derivedState":42,"rootState":"foo"}""", msg) + val d2 = json.decodeFromString(Derived.serializer(), msg) + assertEquals(derived, d2) + } + + @Test + fun canBeSerializedAsParent() { + val derived = Derived(42) + val msg = json.encodeToString(SerializableBase.serializer(), derived) + assertEquals("""{"publicState":"A","privateState":"B"}""", msg) + val d2 = json.decodeFromString(SerializableBase.serializer(), msg) + assertEquals(SerializableBase(), d2) + // no derivedState + assertFailsWithMissingField { json.decodeFromString(Derived.serializer(), msg) } + } + + @Test + fun testWithOpenProperty() { + val d = Derived2("foo") + val msgFull = json.encodeToString(Derived2.serializer(), d) + assertEquals("""{"state1":"foo","state2":"foo"}""", msgFull) + assertEquals("""{"state1":"foo"}""", json.encodeToString(Base1.serializer(), d)) + val restored = json.decodeFromString(Derived2.serializer(), msgFull) + val restored2 = json.decodeFromString(Derived2.serializer(), """{"state1":"bar","state2":"foo"}""") // state1 is ignored anyway + assertEquals("""Derived2(state1='foo')""", restored.toString()) + assertEquals("""Derived2(state1='foo')""", restored2.toString()) + } +} + + + + diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonClassDiscriminatorTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonClassDiscriminatorTest.kt new file mode 100644 index 00000000..5eebe218 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonClassDiscriminatorTest.kt @@ -0,0 +1,113 @@ +/* + * Copyright 2017-2021 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.builtins.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlin.test.* + +class JsonClassDiscriminatorTest : JsonTestBase() { + @Serializable + @JsonClassDiscriminator("sealedType") + sealed class SealedMessage { + @Serializable + @SerialName("SealedMessage.StringMessage") + data class StringMessage(val description: String, val message: String) : SealedMessage() + + @SerialName("EOF") + @Serializable + object EOF : SealedMessage() + } + + @Serializable + @JsonClassDiscriminator("abstractType") + abstract class AbstractMessage { + @Serializable + @SerialName("Message.StringMessage") + data class StringMessage(val description: String, val message: String) : AbstractMessage() + + @Serializable + @SerialName("Message.IntMessage") + data class IntMessage(val description: String, val message: Int) : AbstractMessage() + } + + + @Test + fun testSealedClassesHaveCustomDiscriminator() { + val messages = listOf( + SealedMessage.StringMessage("string message", "foo"), + SealedMessage.EOF + ) + val expected = + """[{"sealedType":"SealedMessage.StringMessage","description":"string message","message":"foo"},{"sealedType":"EOF"}]""" + assertJsonFormAndRestored( + ListSerializer(SealedMessage.serializer()), + messages, + expected, + ) + } + + @Test + fun testAbstractClassesHaveCustomDiscriminator() { + val messages = listOf( + AbstractMessage.StringMessage("string message", "foo"), + AbstractMessage.IntMessage("int message", 42), + ) + val module = SerializersModule { + polymorphic(AbstractMessage::class) { + subclass(AbstractMessage.StringMessage.serializer()) + subclass(AbstractMessage.IntMessage.serializer()) + } + } + val json = Json { serializersModule = module } + val expected = + """[{"abstractType":"Message.StringMessage","description":"string message","message":"foo"},{"abstractType":"Message.IntMessage","description":"int message","message":42}]""" + assertJsonFormAndRestored( + ListSerializer( + AbstractMessage.serializer() + ), messages, expected, json + ) + } + + @Serializable + @JsonClassDiscriminator("message_type") + abstract class Base + + @Serializable + abstract class ErrorClass : Base() + + @Serializable + data class Message(val message: Base, val error: ErrorClass?) + + @Serializable + @SerialName("my.app.BaseMessage") + data class BaseMessage(val message: String) : Base() + + @Serializable + @SerialName("my.app.GenericError") + data class GenericError(@SerialName("error_code") val errorCode: Int) : ErrorClass() + + + @Test + fun testDocumentationInheritanceSample() { + val module = SerializersModule { + polymorphic(Base::class) { + subclass(BaseMessage.serializer()) + } + polymorphic(ErrorClass::class) { + subclass(GenericError.serializer()) + } + } + val json = Json { serializersModule = module } + assertJsonFormAndRestored( + Message.serializer(), + Message(BaseMessage("not found"), GenericError(404)), + """{"message":{"message_type":"my.app.BaseMessage","message":"not found"},"error":{"message_type":"my.app.GenericError","error_code":404}}""", + json + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonEnumsCaseInsensitiveTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonEnumsCaseInsensitiveTest.kt new file mode 100644 index 00000000..0e802c19 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonEnumsCaseInsensitiveTest.kt @@ -0,0 +1,170 @@ +package kotlinx.serialization.features + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.test.* +import kotlin.test.* + +@Suppress("EnumEntryName") +class JsonEnumsCaseInsensitiveTest: JsonTestBase() { + @Serializable + data class Foo( + val one: Bar = Bar.BAZ, + val two: Bar = Bar.QUX, + val three: Bar = Bar.QUX + ) + + enum class Bar { BAZ, QUX } + + // It seems that we no longer report a warning that @Serializable is required for enums with @SerialName. + // It is still required for them to work at top-level. + @Serializable + enum class Cases { + ALL_CAPS, + MiXed, + all_lower, + + @JsonNames("AltName") + hasAltNames, + + @SerialName("SERIAL_NAME") + hasSerialName + } + + @Serializable + data class EnumCases(val cases: List<Cases>) + + val json = Json(default) { decodeEnumsCaseInsensitive = true } + + @Test + fun testCases() = parametrizedTest { mode -> + val input = + """{"cases":["ALL_CAPS","all_caps","mixed","MIXED","miXed","all_lower","ALL_LOWER","all_Lower","hasAltNames","HASALTNAMES","altname","ALTNAME","AltName","SERIAL_NAME","serial_name"]}""" + val target = listOf( + Cases.ALL_CAPS, + Cases.ALL_CAPS, + Cases.MiXed, + Cases.MiXed, + Cases.MiXed, + Cases.all_lower, + Cases.all_lower, + Cases.all_lower, + Cases.hasAltNames, + Cases.hasAltNames, + Cases.hasAltNames, + Cases.hasAltNames, + Cases.hasAltNames, + Cases.hasSerialName, + Cases.hasSerialName + ) + val decoded = json.decodeFromString<EnumCases>(input, mode) + assertEquals(EnumCases(target), decoded) + val encoded = json.encodeToString(decoded, mode) + assertEquals( + """{"cases":["ALL_CAPS","ALL_CAPS","MiXed","MiXed","MiXed","all_lower","all_lower","all_lower","hasAltNames","hasAltNames","hasAltNames","hasAltNames","hasAltNames","SERIAL_NAME","SERIAL_NAME"]}""", + encoded + ) + } + + @Test + fun testTopLevelList() = parametrizedTest { mode -> + val input = """["all_caps","serial_name"]""" + val decoded = json.decodeFromString<List<Cases>>(input, mode) + assertEquals(listOf(Cases.ALL_CAPS, Cases.hasSerialName), decoded) + assertEquals("""["ALL_CAPS","SERIAL_NAME"]""", json.encodeToString(decoded, mode)) + } + + @Test + fun testTopLevelEnum() = parametrizedTest { mode -> + val input = """"altName"""" + val decoded = json.decodeFromString<Cases>(input, mode) + assertEquals(Cases.hasAltNames, decoded) + assertEquals(""""hasAltNames"""", json.encodeToString(decoded, mode)) + } + + @Test + fun testSimpleCase() = parametrizedTest { mode -> + val input = """{"one":"baz","two":"Qux","three":"QUX"}""" + val decoded = json.decodeFromString<Foo>(input, mode) + assertEquals(Foo(), decoded) + assertEquals("""{"one":"BAZ","two":"QUX","three":"QUX"}""", json.encodeToString(decoded, mode)) + } + + enum class E { VALUE_A, @JsonNames("ALTERNATIVE") VALUE_B } + + @Test + fun testDocSample() { + + val j = Json { decodeEnumsCaseInsensitive = true } + @Serializable + data class Outer(val enums: List<E>) + + println(j.decodeFromString<Outer>("""{"enums":["value_A", "alternative"]}""").enums) + } + + @Test + fun testCoercingStillWorks() = parametrizedTest { mode -> + val withCoercing = Json(json) { coerceInputValues = true } + val input = """{"one":"baz","two":"unknown","three":"Que"}""" + assertEquals(Foo(), withCoercing.decodeFromString<Foo>(input, mode)) + } + + @Test + fun testCaseInsensitivePriorityOverCoercing() = parametrizedTest { mode -> + val withCoercing = Json(json) { coerceInputValues = true } + val input = """{"one":"QuX","two":"Baz","three":"Que"}""" + assertEquals(Foo(Bar.QUX, Bar.BAZ, Bar.QUX), withCoercing.decodeFromString<Foo>(input, mode)) + } + + @Test + fun testCoercingStillWorksWithNulls() = parametrizedTest { mode -> + val withCoercing = Json(json) { coerceInputValues = true } + val input = """{"one":"baz","two":"null","three":null}""" + assertEquals(Foo(), withCoercing.decodeFromString<Foo>(input, mode)) + } + + @Test + fun testFeatureDisablesProperly() = parametrizedTest { mode -> + val disabled = Json(json) { + coerceInputValues = true + decodeEnumsCaseInsensitive = false + } + val input = """{"one":"BAZ","two":"BAz","three":"baz"}""" // two and three should be coerced to QUX + assertEquals(Foo(), disabled.decodeFromString<Foo>(input, mode)) + } + + @Test + fun testFeatureDisabledThrowsWithoutCoercing() = parametrizedTest { mode -> + val disabled = Json(json) { + coerceInputValues = false + decodeEnumsCaseInsensitive = false + } + val input = """{"one":"BAZ","two":"BAz","three":"baz"}""" + assertFailsWithMessage<SerializationException>("does not contain element with name 'BAz'") { + disabled.decodeFromString<Foo>(input, mode) + } + } + + @Serializable enum class BadEnum { Bad, BAD } + + @Serializable data class ListBadEnum(val l: List<BadEnum>) + + @Test + fun testLowercaseClashThrowsException() = parametrizedTest { mode -> + assertFailsWithMessage<SerializationException>("""The suggested name 'bad' for enum value BAD is already one of the names for enum value Bad""") { + json.decodeFromString<Box<BadEnum>>("""{"boxed":"bad"}""", mode) + } + assertFailsWithMessage<SerializationException>("""The suggested name 'bad' for enum value BAD is already one of the names for enum value Bad""") { + json.decodeFromString<Box<BadEnum>>("""{"boxed":"unrelated"}""", mode) + } + } + + @Test + fun testLowercaseClashHandledWithoutFeature() = parametrizedTest { mode -> + val disabled = Json(json) { + coerceInputValues = false + decodeEnumsCaseInsensitive = false + } + assertEquals(ListBadEnum(listOf(BadEnum.Bad, BadEnum.BAD)), disabled.decodeFromString("""{"l":["Bad","BAD"]}""", mode)) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonNamesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonNamesTest.kt new file mode 100644 index 00000000..34044191 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonNamesTest.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:Suppress("ReplaceArrayOfWithLiteral") // https://youtrack.jetbrains.com/issue/KT-22578 + +package kotlinx.serialization.features + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class JsonNamesTest : JsonTestBase() { + + @Serializable + data class WithNames(@JsonNames("foo", "_foo") val data: String) + + @Serializable + enum class AlternateEnumNames { + @JsonNames("someValue", "some_value") + VALUE_A, + VALUE_B + } + + @Serializable + data class WithEnumNames( + val enumList: List<AlternateEnumNames>, + val checkCoercion: AlternateEnumNames = AlternateEnumNames.VALUE_B + ) + + @Serializable + data class CollisionWithAlternate( + @JsonNames("_foo") val data: String, + @JsonNames("_foo") val foo: String + ) + + private val inputString1 = """{"foo":"foo"}""" + private val inputString2 = """{"_foo":"foo"}""" + + private fun parameterizedCoercingTest(test: (json: Json, streaming: JsonTestingMode, msg: String) -> Unit) { + for (coercing in listOf(true, false)) { + val json = Json { + coerceInputValues = coercing + useAlternativeNames = true + } + parametrizedTest { streaming -> + test( + json, streaming, + "Failed test with coercing=$coercing and streaming=$streaming" + ) + } + } + } + + @Test + fun testEnumSupportsAlternativeNames() { + val input = """{"enumList":["VALUE_A", "someValue", "some_value", "VALUE_B"], "checkCoercion":"someValue"}""" + val expected = WithEnumNames( + listOf( + AlternateEnumNames.VALUE_A, + AlternateEnumNames.VALUE_A, + AlternateEnumNames.VALUE_A, + AlternateEnumNames.VALUE_B + ), AlternateEnumNames.VALUE_A + ) + parameterizedCoercingTest { json, streaming, msg -> + assertEquals(expected, json.decodeFromString(input, streaming), msg) + } + } + + @Test + fun topLevelEnumSupportAlternativeNames() { + parameterizedCoercingTest { json, streaming, msg -> + assertEquals(AlternateEnumNames.VALUE_A, json.decodeFromString("\"someValue\"", streaming), msg) + } + } + + @Test + fun testParsesAllAlternativeNames() { + for (input in listOf(inputString1, inputString2)) { + parameterizedCoercingTest { json, streaming, _ -> + val data = json.decodeFromString(WithNames.serializer(), input, jsonTestingMode = streaming) + assertEquals("foo", data.data, "Failed to parse input '$input' with streaming=$streaming") + } + } + } + + @Test + fun testThrowsAnErrorOnDuplicateNames() { + val serializer = CollisionWithAlternate.serializer() + parameterizedCoercingTest { json, streaming, _ -> + assertFailsWithMessage<SerializationException>( + """The suggested name '_foo' for property foo is already one of the names for property data""", + "Class ${serializer.descriptor.serialName} did not fail with streaming=$streaming" + ) { + json.decodeFromString( + serializer, inputString2, + jsonTestingMode = streaming + ) + } + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonNamingStrategyExclusionTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonNamingStrategyExclusionTest.kt new file mode 100644 index 00000000..b9974543 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonNamingStrategyExclusionTest.kt @@ -0,0 +1,60 @@ +package kotlinx.serialization.features + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlin.test.* + +class JsonNamingStrategyExclusionTest : JsonTestBase() { + @SerialInfo + @Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY) + annotation class OriginalSerialName + + private fun List<Annotation>.hasOriginal() = filterIsInstance<OriginalSerialName>().isNotEmpty() + + private val myStrategy = JsonNamingStrategy { descriptor, index, serialName -> + if (descriptor.annotations.hasOriginal() || descriptor.getElementAnnotations(index).hasOriginal()) serialName + else JsonNamingStrategy.SnakeCase.serialNameForJson(descriptor, index, serialName) + } + + @Serializable + @OriginalSerialName + data class Foo(val firstArg: String = "a", val secondArg: String = "b") + + enum class E { + @OriginalSerialName + FIRST_E, + SECOND_E + } + + @Serializable + data class Bar( + val firstBar: String = "a", + @OriginalSerialName val secondBar: String = "b", + val fooBar: Foo = Foo(), + val enumBarOne: E = E.FIRST_E, + val enumBarTwo: E = E.SECOND_E + ) + + private fun doTest(json: Json) { + val j = Json(json) { + namingStrategy = myStrategy + } + val bar = Bar() + assertJsonFormAndRestored( + Bar.serializer(), + bar, + """{"first_bar":"a","secondBar":"b","foo_bar":{"firstArg":"a","secondArg":"b"},"enum_bar_one":"FIRST_E","enum_bar_two":"SECOND_E"}""", + j + ) + } + + @Test + fun testJsonNamingStrategyWithAlternativeNames() = doTest(Json(default) { + useAlternativeNames = true + }) + + @Test + fun testJsonNamingStrategyWithoutAlternativeNames() = doTest(Json(default) { + useAlternativeNames = false + }) +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonNamingStrategyTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonNamingStrategyTest.kt new file mode 100644 index 00000000..9e2cf1d4 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonNamingStrategyTest.kt @@ -0,0 +1,242 @@ +package kotlinx.serialization.features + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlinx.serialization.json.* +import kotlinx.serialization.test.* +import kotlin.test.* + + +class JsonNamingStrategyTest : JsonTestBase() { + @Serializable + data class Foo( + val simple: String = "a", + val oneWord: String = "b", + val already_in_snake: String = "c", + val aLotOfWords: String = "d", + val FirstCapitalized: String = "e", + val hasAcronymURL: Bar = Bar.BAZ, + val hasDigit123AndPostfix: Bar = Bar.QUX, + val coercionTest: Bar = Bar.QUX + ) + + enum class Bar { BAZ, QUX } + + val jsonWithNaming = Json(default) { + namingStrategy = JsonNamingStrategy.SnakeCase + decodeEnumsCaseInsensitive = true // check that related feature does not break anything + } + + @Test + fun testJsonNamingStrategyWithAlternativeNames() = doTest(Json(jsonWithNaming) { + useAlternativeNames = true + }) + + @Test + fun testJsonNamingStrategyWithoutAlternativeNames() = doTest(Json(jsonWithNaming) { + useAlternativeNames = false + }) + + private fun doTest(json: Json) { + val foo = Foo() + assertJsonFormAndRestored( + Foo.serializer(), + foo, + """{"simple":"a","one_word":"b","already_in_snake":"c","a_lot_of_words":"d","first_capitalized":"e","has_acronym_url":"BAZ","has_digit123_and_postfix":"QUX","coercion_test":"QUX"}""", + json + ) + } + + @Test + fun testNamingStrategyWorksWithCoercing() { + val j = Json(jsonWithNaming) { + coerceInputValues = true + useAlternativeNames = false + } + assertEquals( + Foo(), + j.decodeFromString("""{"simple":"a","one_word":"b","already_in_snake":"c","a_lot_of_words":"d","first_capitalized":"e","has_acronym_url":"baz","has_digit123_and_postfix":"qux","coercion_test":"invalid"}""") + ) + } + + @Test + fun testSnakeCaseStrategy() { + fun apply(name: String) = + JsonNamingStrategy.SnakeCase.serialNameForJson(String.serializer().descriptor, 0, name) + + val cases = mapOf<String, String>( + "" to "", + "_" to "_", + "___" to "___", + "a" to "a", + "A" to "a", + "_1" to "_1", + "_a" to "_a", + "_A" to "_a", + "property" to "property", + "twoWords" to "two_words", + "threeDistinctWords" to "three_distinct_words", + "ThreeDistinctWords" to "three_distinct_words", + "Oneword" to "oneword", + "camel_Case_Underscores" to "camel_case_underscores", + "_many____underscores__" to "_many____underscores__", + "URLmapping" to "ur_lmapping", + "URLMapping" to "url_mapping", + "IOStream" to "io_stream", + "IOstream" to "i_ostream", + "myIo2Stream" to "my_io2_stream", + "myIO2Stream" to "my_io2_stream", + "myIO2stream" to "my_io2stream", + "myIO2streamMax" to "my_io2stream_max", + "InURLBetween" to "in_url_between", + "myHTTP2APIKey" to "my_http2_api_key", + "myHTTP2fastApiKey" to "my_http2fast_api_key", + "myHTTP23APIKey" to "my_http23_api_key", + "myHttp23ApiKey" to "my_http23_api_key", + "theWWW" to "the_www", + "theWWW_URL_xxx" to "the_www_url_xxx", + "hasDigit123AndPostfix" to "has_digit123_and_postfix" + ) + + cases.forEach { (input, expected) -> + assertEquals(expected, apply(input)) + } + } + + @Test + fun testKebabCaseStrategy() { + fun apply(name: String) = + JsonNamingStrategy.KebabCase.serialNameForJson(String.serializer().descriptor, 0, name) + + val cases = mapOf<String, String>( + "" to "", + "_" to "_", + "-" to "-", + "___" to "___", + "---" to "---", + "a" to "a", + "A" to "a", + "-1" to "-1", + "-a" to "-a", + "-A" to "-a", + "property" to "property", + "twoWords" to "two-words", + "threeDistinctWords" to "three-distinct-words", + "ThreeDistinctWords" to "three-distinct-words", + "Oneword" to "oneword", + "camel-Case-WithDashes" to "camel-case-with-dashes", + "_many----dashes--" to "_many----dashes--", + "URLmapping" to "ur-lmapping", + "URLMapping" to "url-mapping", + "IOStream" to "io-stream", + "IOstream" to "i-ostream", + "myIo2Stream" to "my-io2-stream", + "myIO2Stream" to "my-io2-stream", + "myIO2stream" to "my-io2stream", + "myIO2streamMax" to "my-io2stream-max", + "InURLBetween" to "in-url-between", + "myHTTP2APIKey" to "my-http2-api-key", + "myHTTP2fastApiKey" to "my-http2fast-api-key", + "myHTTP23APIKey" to "my-http23-api-key", + "myHttp23ApiKey" to "my-http23-api-key", + "theWWW" to "the-www", + "theWWW-URL-xxx" to "the-www-url-xxx", + "hasDigit123AndPostfix" to "has-digit123-and-postfix" + ) + + cases.forEach { (input, expected) -> + assertEquals(expected, apply(input)) + } + } + + @Serializable + data class DontUseOriginal(val testCase: String) + + @Test + fun testNamingStrategyOverridesOriginal() { + val json = Json(jsonWithNaming) { + ignoreUnknownKeys = true + } + parametrizedTest { mode -> + assertEquals(DontUseOriginal("a"), json.decodeFromString("""{"test_case":"a","testCase":"b"}""", mode)) + } + + val jsonThrows = Json(jsonWithNaming) { + ignoreUnknownKeys = false + } + parametrizedTest { mode -> + assertFailsWithMessage<SerializationException>("Encountered an unknown key 'testCase'") { + jsonThrows.decodeFromString<DontUseOriginal>("""{"test_case":"a","testCase":"b"}""", mode) + } + } + } + + @Serializable + data class CollisionCheckPrimary(val testCase: String, val test_case: String) + + @Serializable + data class CollisionCheckAlternate(val testCase: String, @JsonNames("test_case") val testCase2: String) + + @Test + fun testNamingStrategyPrioritizesOverAlternative() { + val json = Json(jsonWithNaming) { + ignoreUnknownKeys = true + } + parametrizedTest { mode -> + assertFailsWithMessage<SerializationException>("The suggested name 'test_case' for property test_case is already one of the names for property testCase") { + json.decodeFromString<CollisionCheckPrimary>("""{"test_case":"a"}""", mode) + } + } + parametrizedTest { mode -> + assertFailsWithMessage<SerializationException>("The suggested name 'test_case' for property testCase2 is already one of the names for property testCase") { + json.decodeFromString<CollisionCheckAlternate>("""{"test_case":"a"}""", mode) + } + } + } + + + @Serializable + data class OriginalAsFallback(@JsonNames("testCase") val testCase: String) + + @Test + fun testCanUseOriginalNameAsAlternative() { + val json = Json(jsonWithNaming) { + ignoreUnknownKeys = true + } + parametrizedTest { mode -> + assertEquals(OriginalAsFallback("b"), json.decodeFromString("""{"testCase":"b"}""", mode)) + } + } + + @Serializable + sealed interface SealedBase { + @Serializable + @JsonClassDiscriminator("typeSub") + sealed class SealedMid : SealedBase { + @Serializable + @SerialName("SealedSub1") + object SealedSub1 : SealedMid() + } + + @Serializable + @SerialName("SealedSub2") + data class SealedSub2(val testCase: Int = 0) : SealedBase + } + + @Serializable + data class Holder(val testBase: SealedBase, val testMid: SealedBase.SealedMid) + + @Test + fun testNamingStrategyDoesNotAffectPolymorphism() { + val json = Json(jsonWithNaming) { + classDiscriminator = "typeBase" + } + val holder = Holder(SealedBase.SealedSub2(), SealedBase.SealedMid.SealedSub1) + assertJsonFormAndRestored( + Holder.serializer(), + holder, + """{"test_base":{"typeBase":"SealedSub2","test_case":0},"test_mid":{"typeSub":"SealedSub1"}}""", + json + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/LocalClassesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/LocalClassesTest.kt new file mode 100644 index 00000000..82a5ca64 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/LocalClassesTest.kt @@ -0,0 +1,84 @@ +package kotlinx.serialization.features + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class LocalClassesTest { + object ObjectCustomSerializer: KSerializer<Any?> { + override val descriptor: SerialDescriptor = buildSerialDescriptor("tmp", PrimitiveKind.INT) + override fun serialize(encoder: Encoder, value: Any?) { + encoder.encodeNull() + } + + override fun deserialize(decoder: Decoder): Any? { + return decoder.decodeNull() + } + } + + class ClassCustomSerializer: KSerializer<Any?> { + override val descriptor: SerialDescriptor = buildSerialDescriptor("tmp", PrimitiveKind.INT) + override fun serialize(encoder: Encoder, value: Any?) { + encoder.encodeNull() + } + + override fun deserialize(decoder: Decoder): Any? { + return decoder.decodeNull() + } + } + + @Test + fun testGeneratedSerializer() { + @Serializable + data class Local(val i: Int) + + val origin = Local(42) + + val decoded: Local = Json.decodeFromString(Json.encodeToString(origin)) + assertEquals(origin, decoded) + } + + @Test + fun testInLambda() { + 42.let { + @Serializable + data class Local(val i: Int) + + val origin = Local(it) + + val decoded: Local = Json.decodeFromString(Json.encodeToString(origin)) + assertEquals(origin, decoded) + } + } + + @Suppress("SERIALIZER_TYPE_INCOMPATIBLE") + @Test + fun testObjectCustomSerializer() { + @Serializable(with = ObjectCustomSerializer::class) + data class Local(val i: Int) + + val origin: Local? = null + + val decoded: Local? = Json.decodeFromString(Json.encodeToString(origin)) + assertEquals(origin, decoded) + } + + @Suppress("SERIALIZER_TYPE_INCOMPATIBLE") + @Test + fun testClassCustomSerializer() { + @Serializable(with = ClassCustomSerializer::class) + data class Local(val i: Int) + + val origin: Local? = null + + // FIXME change to `noLegacyJs` when lookup of `ClassCustomSerializer` will work on Native and JS/IR + jvmOnly { + val decoded: Local? = Json.decodeFromString(Json.encodeToString(origin)) + assertEquals(origin, decoded) + } + } + +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/LongAsStringTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/LongAsStringTest.kt new file mode 100644 index 00000000..c459b6a3 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/LongAsStringTest.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2017-2020 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.builtins.* +import kotlinx.serialization.json.* +import kotlin.test.* + +class LongAsStringTest : JsonTestBase() { + @Serializable + data class HasLong(@Serializable(LongAsStringSerializer::class) val l: Long) + + @Test + fun canSerializeAsStringAndParseBack() = parametrizedTest { jsonTestingMode -> + val original = HasLong(Long.MAX_VALUE - 1) + val str = default.encodeToString(HasLong.serializer(), original, jsonTestingMode) + assertEquals("""{"l":"9223372036854775806"}""", str) + val restored = default.decodeFromString(HasLong.serializer(), str, jsonTestingMode) + assertEquals(original, restored) + } + + @Test + fun canNotDeserializeInvalidString() = parametrizedTest { jsonTestingMode -> + val str = """{"l": "this is definitely not a long"}""" + assertFailsWith<NumberFormatException> { default.decodeFromString(HasLong.serializer(), str, jsonTestingMode) } + val str2 = """{"l": "1000000000000000000000"}""" // toooo long for Long + assertFailsWith<NumberFormatException> { default.decodeFromString(HasLong.serializer(), str2, jsonTestingMode) } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/MetaSerializableJsonTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/MetaSerializableJsonTest.kt new file mode 100644 index 00000000..af9ef6c2 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/MetaSerializableJsonTest.kt @@ -0,0 +1,72 @@ +package kotlinx.serialization.features + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlin.test.* + +@MetaSerializable +@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS) +annotation class JsonComment(val comment: String) + +@JsonComment("class_comment") +data class IntDataCommented(val i: Int) + +class MetaSerializableJsonTest : JsonTestBase() { + + @Serializable + data class Carrier( + val plain: String, + @JsonComment("string_comment") val commented: StringData, + val intData: IntDataCommented + ) + + class CarrierSerializer : JsonTransformingSerializer<Carrier>(serializer()) { + + private val desc = Carrier.serializer().descriptor + private fun List<Annotation>.comment(): String? = filterIsInstance<JsonComment>().firstOrNull()?.comment + + private val commentMap = (0 until desc.elementsCount).associateBy({ desc.getElementName(it) }, + { desc.getElementAnnotations(it).comment() ?: desc.getElementDescriptor(it).annotations.comment() }) + + // NB: we may want to add this to public API + private fun JsonElement.editObject(action: (MutableMap<String, JsonElement>) -> Unit): JsonElement { + val mutable = this.jsonObject.toMutableMap() + action(mutable) + return JsonObject(mutable) + } + + override fun transformDeserialize(element: JsonElement): JsonElement { + return element.editObject { result -> + for ((key, value) in result) { + commentMap[key]?.let { + result[key] = value.editObject { + it.remove("comment") + } + } + } + } + } + + override fun transformSerialize(element: JsonElement): JsonElement { + return element.editObject { result -> + for ((key, value) in result) { + commentMap[key]?.let { comment -> + result[key] = value.editObject { + it["comment"] = JsonPrimitive(comment) + } + } + } + } + } + } + + @Test + fun testMyJsonComment() { + assertJsonFormAndRestored( + CarrierSerializer(), + Carrier("plain", StringData("string1"), IntDataCommented(42)), + """{"plain":"plain","commented":{"data":"string1","comment":"string_comment"},"intData":{"i":42,"comment":"class_comment"}}""" + ) + } + +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/ObjectSerialization.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/ObjectSerialization.kt new file mode 100644 index 00000000..da5919c1 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/ObjectSerialization.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2017-2019 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 kotlinx.serialization.modules.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class ObjectSerializationTest : JsonTestBase() { + + sealed class ApiResponse { + @Serializable + @SerialName("ApiError") + object Error : ApiResponse() + + @Serializable + @SerialName("ApiResponse") + data class Response(val message: String) : ApiResponse() + } + + @Serializable + data class ApiCarrier(@Polymorphic val response: ApiResponse) + + val module = SerializersModule { + polymorphic(ApiResponse::class) { + subclass(ApiResponse.Error.serializer()) + subclass(ApiResponse.Response.serializer()) + } + } + + val json = Json { serializersModule = module } + + @Test + fun testSealedClassSerialization() { + val carrier1 = ApiCarrier(ApiResponse.Error) + val carrier2 = ApiCarrier(ApiResponse.Response("OK")) + assertJsonFormAndRestored(ApiCarrier.serializer(), carrier1, """{"response":{"type":"ApiError"}}""", json) + assertJsonFormAndRestored( + ApiCarrier.serializer(), + carrier2, + """{"response":{"type":"ApiResponse","message":"OK"}}""", + json + ) + } + + @Test + fun testUnknownKeys() { + val string = """{"metadata":"foo"}""" + assertFailsWithMessage<SerializationException>("ignoreUnknownKeys") { + Json.decodeFromString( + ApiResponse.Error.serializer(), + string + ) + } + val json = Json { ignoreUnknownKeys = true } + assertEquals(ApiResponse.Error, json.decodeFromString(ApiResponse.Error.serializer(), string)) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/PartiallyCustomSerializerTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PartiallyCustomSerializerTest.kt new file mode 100644 index 00000000..9ad4afec --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PartiallyCustomSerializerTest.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2017-2020 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.encoding.* +import kotlinx.serialization.json.Json +import kotlin.test.Test +import kotlin.test.assertEquals + +@Serializable(WithNull.Companion::class) +data class WithNull(@SerialName("value") val nullable: String? = null) { + @Serializer(forClass = WithNull::class) + companion object : KSerializer<WithNull> { + override fun serialize(encoder: Encoder, value: WithNull) { + val elemOutput = encoder.beginStructure(descriptor) + if (value.nullable != null) elemOutput.encodeStringElement(descriptor, 0, value.nullable) + elemOutput.endStructure(descriptor) + } + } +} + +class PartiallyCustomSerializerTest { + @Test + fun partiallyCustom() { + assertEquals("""{"value":"foo"}""", Json.encodeToString(WithNull.serializer(), WithNull("foo"))) + assertEquals("""{}""", Json.encodeToString(WithNull.serializer(), WithNull())) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphicDeserializationErrorMessagesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphicDeserializationErrorMessagesTest.kt new file mode 100644 index 00000000..2b2f1f70 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphicDeserializationErrorMessagesTest.kt @@ -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'") + }) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphicOnClassesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphicOnClassesTest.kt new file mode 100644 index 00000000..77004dbc --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphicOnClassesTest.kt @@ -0,0 +1,149 @@ +/* + * Copyright 2017-2021 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.descriptors.* +import kotlinx.serialization.internal.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlinx.serialization.test.* +import kotlin.reflect.* +import kotlin.test.* + +class PolymorphicOnClassesTest { + + // this has implicit @Polymorphic + interface IMessage { + val body: String + } + + // and this class too has implicit @Polymorphic + @Serializable + abstract class Message : IMessage { + abstract override val body: String + } + + @Polymorphic + @Serializable + @SerialName("SimpleMessage") // to cut out package prefix + open class SimpleMessage : Message() { + override var body: String = "Simple" + } + + @Serializable + @SerialName("DoubleSimpleMessage") + class DoubleSimpleMessage(val body2: String) : SimpleMessage() + + @Serializable + @SerialName("MessageWithId") + open class MessageWithId(val id: Int, override val body: String) : Message() + + @Serializable + class Holder( + val iMessage: IMessage, + val iMessageList: List<IMessage>, + val message: Message, + val msgSet: Set<Message>, + val simple: SimpleMessage, + // all above should be polymorphic + val withId: MessageWithId // but this not + ) + + + private fun genTestData(): Holder { + var cnt = -1 + fun gen(): MessageWithId { + cnt++ + return MessageWithId(cnt, "Message #$cnt") + } + + return Holder(gen(), listOf(gen(), gen()), gen(), setOf(SimpleMessage()), DoubleSimpleMessage("DoubleSimple"), gen()) + } + + @Suppress("UNCHECKED_CAST") + private val testModule = SerializersModule { + listOf(Message::class, IMessage::class, SimpleMessage::class).forEach { clz -> + polymorphic(clz as KClass<IMessage>) { + subclass(SimpleMessage.serializer()) + subclass(DoubleSimpleMessage.serializer()) + subclass(MessageWithId.serializer()) + } + } + } + + @Test + fun testEnablesImplicitlyOnInterfacesAndAbstractClasses() { + val json = Json { + prettyPrint = false + useArrayPolymorphism = true + serializersModule = testModule + encodeDefaults = true + } + val data = genTestData() + assertEquals( + """{"iMessage":["MessageWithId",{"id":0,"body":"Message #0"}],""" + + """"iMessageList":[["MessageWithId",{"id":1,"body":"Message #1"}],""" + + """["MessageWithId",{"id":2,"body":"Message #2"}]],"message":["MessageWithId",{"id":3,"body":"Message #3"}],""" + + """"msgSet":[["SimpleMessage",{"body":"Simple"}]],"simple":["DoubleSimpleMessage",{"body":"Simple",""" + + """"body2":"DoubleSimple"}],"withId":{"id":4,"body":"Message #4"}}""", + json.encodeToString(Holder.serializer(), data) + ) + } + + @Test + fun testDescriptor() { + val polyDesc = Holder.serializer().descriptor.elementDescriptors.first() + assertEquals(PolymorphicSerializer(IMessage::class).descriptor, polyDesc) + assertEquals(2, polyDesc.elementsCount) + assertEquals(PrimitiveKind.STRING, polyDesc.getElementDescriptor(0).kind) + } + + private fun SerialDescriptor.inContext(module: SerializersModule): List<SerialDescriptor> = when (kind) { + PolymorphicKind.OPEN -> module.getPolymorphicDescriptors(this) + else -> error("Expected this function to be called on OPEN descriptor") + } + + @Test + fun testResolvePolymorphicDescriptor() { + val polyDesc = Holder.serializer().descriptor.elementDescriptors.first() // iMessage: IMessage + + assertEquals(PolymorphicKind.OPEN, polyDesc.kind) + + val inheritors = polyDesc.inContext(testModule) + val names = listOf("SimpleMessage", "DoubleSimpleMessage", "MessageWithId").toSet() + assertEquals(names, inheritors.map(SerialDescriptor::serialName).toSet(), "Expected correct inheritor names") + assertTrue(inheritors.all { it.kind == StructureKind.CLASS }, "Expected all inheritors to be CLASS") + } + + @Test + fun testDocSampleWithAllDistinct() { + fun allDistinctNames(descriptor: SerialDescriptor, module: SerializersModule) = when (descriptor.kind) { + is PolymorphicKind.OPEN -> module.getPolymorphicDescriptors(descriptor) + .map { it.elementNames.toList() }.flatten().toSet() + is SerialKind.CONTEXTUAL -> module.getContextualDescriptor(descriptor)?.elementNames?.toList().orEmpty().toSet() + else -> descriptor.elementNames.toSet() + } + + val polyDesc = Holder.serializer().descriptor.elementDescriptors.first() // iMessage: IMessage + assertEquals(setOf("id", "body", "body2"), allDistinctNames(polyDesc, testModule)) + assertEquals(setOf("id", "body"), allDistinctNames(MessageWithId.serializer().descriptor, testModule)) + } + + @Test + fun testSerializerLookupForInterface() { + // On JVM and JS IR it can be supported via reflection/runtime hacks + // on Native, unfortunately, only with intrinsics. + if (isNative() || isWasm()) return + val msgSer = serializer<IMessage>() + assertEquals(IMessage::class, (msgSer as AbstractPolymorphicSerializer).baseClass) + } + + @Test + fun testSerializerLookupForAbstractClass() { + val absSer = serializer<Message>() + assertEquals(Message::class, (absSer as AbstractPolymorphicSerializer).baseClass) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphismTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphismTest.kt new file mode 100644 index 00000000..c4938871 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphismTest.kt @@ -0,0 +1,184 @@ +/* + * Copyright 2017-2020 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.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class PolymorphismTest : JsonTestBase() { + + @Serializable + data class Wrapper( + @Id(1) @Polymorphic val polyBase1: PolyBase, + @Id(2) @Polymorphic val polyBase2: PolyBase + ) + + private val module: SerializersModule = BaseAndDerivedModule + SerializersModule { + polymorphic( + PolyDerived::class, + PolyDerived.serializer() + ) + } + + private val json = Json { useArrayPolymorphism = true; serializersModule = module } + + @Test + fun testInheritanceJson() = parametrizedTest { jsonTestingMode -> + val obj = Wrapper( + PolyBase(2), + PolyDerived("b") + ) + val bytes = json.encodeToString(Wrapper.serializer(), obj, jsonTestingMode) + assertEquals( + """{"polyBase1":["kotlinx.serialization.PolyBase",{"id":2}],""" + + """"polyBase2":["kotlinx.serialization.PolyDerived",{"id":1,"s":"b"}]}""", bytes + ) + } + + @Test + fun testSerializeWithExplicitPolymorphicSerializer() = parametrizedTest { jsonTestingMode -> + val obj = PolyDerived("b") + val s = json.encodeToString(PolymorphicSerializer(PolyDerived::class), obj, jsonTestingMode) + assertEquals("""["kotlinx.serialization.PolyDerived",{"id":1,"s":"b"}]""", s) + } + + object PolyDefaultDeserializer : JsonTransformingSerializer<PolyDefault>(PolyDefault.serializer()) { + override fun transformDeserialize(element: JsonElement): JsonElement = buildJsonObject { + put("json", JsonObject(element.jsonObject.filterKeys { it != "type" })) + put("id", 42) + } + } + + object EvenDefaultSerializer : SerializationStrategy<PolyBase> { + override val descriptor = buildClassSerialDescriptor("even") { + element<String>("parity") + } + + override fun serialize(encoder: Encoder, value: PolyBase) { + encoder.encodeStructure(descriptor) { + encodeStringElement(descriptor, 0, "even") + } + } + } + + object OddDefaultSerializer : SerializationStrategy<PolyBase> { + override val descriptor = buildClassSerialDescriptor("odd") { + element<String>("parity") + } + + override fun serialize(encoder: Encoder, value: PolyBase) { + encoder.encodeStructure(descriptor) { + encodeStringElement(descriptor, 0, "odd") + } + } + } + + @Test + fun testDefaultDeserializer() = parametrizedTest { jsonTestingMode -> + val withDefault = module + SerializersModule { + polymorphicDefaultDeserializer(PolyBase::class) { name -> + if (name == "foo") { + PolyDefaultDeserializer + } else { + null + } + } + } + + val adjustedJson = Json { serializersModule = withDefault } + val string = """ + {"polyBase1":{"type":"kotlinx.serialization.PolyBase","id":239}, + "polyBase2":{"type":"foo","key":42}}""".trimIndent() + val result = adjustedJson.decodeFromString(Wrapper.serializer(), string, jsonTestingMode) + assertEquals(Wrapper(PolyBase(239), PolyDefault(JsonObject(mapOf("key" to JsonPrimitive(42))))), result) + + val replaced = string.replace("foo", "bar") + assertFailsWithMessage<SerializationException>("not found") { adjustedJson.decodeFromString(Wrapper.serializer(), replaced, jsonTestingMode) } + } + + @Test + fun testDefaultDeserializerForMissingDiscriminator() = parametrizedTest { jsonTestingMode -> + val json = Json { + serializersModule = module + SerializersModule { + polymorphicDefaultDeserializer(PolyBase::class) { name -> + if (name == null) { + PolyDefaultDeserializer + } else { + null + } + } + } + } + val string = """ + {"polyBase1":{"type":"kotlinx.serialization.PolyBase","id":239}, + "polyBase2":{"key":42}}""".trimIndent() + val result = json.decodeFromString(Wrapper.serializer(), string, jsonTestingMode) + assertEquals(Wrapper(PolyBase(239), PolyDefault(JsonObject(mapOf("key" to JsonPrimitive(42))))), result) + } + + @Test + fun testDefaultSerializer() = parametrizedTest { jsonTestingMode -> + val json = Json { + serializersModule = module + SerializersModule { + polymorphicDefaultSerializer(PolyBase::class) { value -> + if (value.id % 2 == 0) { + EvenDefaultSerializer + } else { + OddDefaultSerializer + } + } + } + } + val obj = Wrapper( + PolyDefaultWithId(0), + PolyDefaultWithId(1) + ) + val s = json.encodeToString(Wrapper.serializer(), obj, jsonTestingMode) + assertEquals("""{"polyBase1":{"type":"even","parity":"even"},"polyBase2":{"type":"odd","parity":"odd"}}""", s) + } + + @Serializable + sealed class Conf { + @Serializable + @SerialName("empty") + object Empty : Conf() // default + + @Serializable + @SerialName("simple") + data class Simple(val value: String) : Conf() + } + + private val jsonForConf = Json { + isLenient = false + ignoreUnknownKeys = true + serializersModule = SerializersModule { + polymorphicDefaultDeserializer(Conf::class) { Conf.Empty.serializer() } + } + } + + @Test + fun defaultSerializerWithEmptyBodyTest() = parametrizedTest { mode -> + assertEquals(Conf.Simple("123"), jsonForConf.decodeFromString<Conf>("""{"type": "simple", "value": "123"}""", mode)) + assertEquals(Conf.Empty, jsonForConf.decodeFromString<Conf>("""{"type": "default"}""", mode)) + assertEquals(Conf.Empty, jsonForConf.decodeFromString<Conf>("""{"unknown": "Meow"}""", mode)) + assertEquals(Conf.Empty, jsonForConf.decodeFromString<Conf>("""{}""", mode)) + } + + @Test + fun testTypeKeysInLenientMode() = parametrizedTest { mode -> + val json = Json(jsonForConf) { isLenient = true } + + assertEquals(Conf.Simple("123"), json.decodeFromString<Conf>("""{type: simple, value: 123}""", mode)) + assertEquals(Conf.Empty, json.decodeFromString<Conf>("""{type: default}""", mode)) + assertEquals(Conf.Empty, json.decodeFromString<Conf>("""{unknown: Meow}""", mode)) + assertEquals(Conf.Empty, json.decodeFromString<Conf>("""{}""", mode)) + + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphismWithAnyTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphismWithAnyTest.kt new file mode 100644 index 00000000..07b6e31a --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphismWithAnyTest.kt @@ -0,0 +1,149 @@ +/* + * Copyright 2017-2019 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 kotlinx.serialization.modules.* +import kotlinx.serialization.modules.plus +import kotlinx.serialization.test.assertStringFormAndRestored +import kotlin.test.* + +class PolymorphismWithAnyTest: JsonTestBase() { + + @Serializable + data class MyPolyData(val data: Map<String, @Polymorphic Any>) + + @Serializable + data class MyPolyDataWithPolyBase( + val data: Map<String, @Polymorphic Any>, + @Polymorphic val polyBase: PolyBase + ) + + // KClass.toString() on JS prints simple name, not FQ one + @Suppress("NAME_SHADOWING") + private fun checkNotRegisteredMessage(className: String, scopeName: String, exception: SerializationException) { + val className = className.substringAfterLast('.') + val scopeName = scopeName.substringAfterLast('.') + val expectedText = + "Serializer for subclass '$className' is not found in the polymorphic scope of '$scopeName'" + assertTrue(exception.message!!.startsWith(expectedText), + "Found $exception, but expected to start with: $expectedText") + } + + @Test + fun testFailWithoutModulesWithCustomClass() = parametrizedTest { mode -> + checkNotRegisteredMessage( + "kotlinx.serialization.IntData", "kotlin.Any", + assertFailsWith<SerializationException>("not registered") { + Json.encodeToString( + MyPolyData.serializer(), + MyPolyData(mapOf("a" to IntData(42))), + mode + ) + } + ) + } + + @Test + fun testWithModules() { + val json = Json { + serializersModule = SerializersModule { polymorphic(Any::class) { subclass(IntData.serializer()) } } + } + assertJsonFormAndRestored( + expected = """{"data":{"a":{"type":"kotlinx.serialization.IntData","intV":42}}}""", + data = MyPolyData(mapOf("a" to IntData(42))), + serializer = MyPolyData.serializer(), + json = json + ) + } + + /** + * This test should fail because PolyDerived registered in the scope of PolyBase, not kotlin.Any + */ + @Test + fun testFailWithModulesNotInAnyScope() = parametrizedTest { mode -> + val json = Json { serializersModule = BaseAndDerivedModule } + checkNotRegisteredMessage( + "kotlinx.serialization.PolyDerived", "kotlin.Any", + assertFailsWith<SerializationException> { + json.encodeToString( + MyPolyData.serializer(), + MyPolyData(mapOf("a" to PolyDerived("foo"))), + mode + ) + } + ) + } + + private val baseAndDerivedModuleAtAny = SerializersModule { + polymorphic(Any::class) { + subclass(PolyDerived.serializer()) + } + } + + + @Test + fun testRebindModules() { + val json = Json { serializersModule = baseAndDerivedModuleAtAny } + assertJsonFormAndRestored( + expected = """{"data":{"a":{"type":"kotlinx.serialization.PolyDerived","id":1,"s":"foo"}}}""", + data = MyPolyData(mapOf("a" to PolyDerived("foo"))), + serializer = MyPolyData.serializer(), + json = json + ) + } + + /** + * This test should fail because PolyDerived registered in the scope of kotlin.Any, not PolyBase + */ + @Test + fun testFailWithModulesNotInParticularScope() = parametrizedTest { mode -> + val json = Json { serializersModule = baseAndDerivedModuleAtAny } + checkNotRegisteredMessage( + "kotlinx.serialization.PolyDerived", "kotlinx.serialization.PolyBase", + assertFailsWith { + json.encodeToString( + MyPolyDataWithPolyBase.serializer(), + MyPolyDataWithPolyBase( + mapOf("a" to PolyDerived("foo")), + PolyDerived("foo") + ), + mode + ) + } + ) + } + + @Test + fun testBindModules() { + val json = Json { serializersModule = (baseAndDerivedModuleAtAny + BaseAndDerivedModule) } + assertJsonFormAndRestored( + expected = """{"data":{"a":{"type":"kotlinx.serialization.PolyDerived","id":1,"s":"foo"}}, + |"polyBase":{"type":"kotlinx.serialization.PolyDerived","id":1,"s":"foo"}}""".trimMargin().lines().joinToString( + "" + ), + data = MyPolyDataWithPolyBase( + mapOf("a" to PolyDerived("foo")), + PolyDerived("foo") + ), + serializer = MyPolyDataWithPolyBase.serializer(), + json = json + ) + } + + @Test + fun testTypeKeyLastInInput() = parametrizedTest { mode -> + val json = Json { serializersModule = (baseAndDerivedModuleAtAny + BaseAndDerivedModule) } + val input = """{"data":{"a":{"id":1,"s":"foo","type":"kotlinx.serialization.PolyDerived"}}, + |"polyBase":{"id":1,"s":"foo","type":"kotlinx.serialization.PolyDerived"}}""".trimMargin().lines().joinToString( + "") + val data = MyPolyDataWithPolyBase( + mapOf("a" to PolyDerived("foo")), + PolyDerived("foo") + ) + assertEquals(data, json.decodeFromString(MyPolyDataWithPolyBase.serializer(), input, mode)) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/PrimitiveArraySerializersTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PrimitiveArraySerializersTest.kt new file mode 100644 index 00000000..2397c177 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PrimitiveArraySerializersTest.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2017-2019 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 kotlinx.serialization.test.* +import kotlin.test.* + +class PrimitiveArraySerializersTest : JsonTestBase() { + + @Serializable + data class A( + val arr: ByteArray, + val arr2: IntArray = intArrayOf(1, 2), + val arr3: BooleanArray = booleanArrayOf(true, false), + var arr4: CharArray = charArrayOf('a', 'b', 'c'), + val arr5: DoubleArray = doubleArrayOf(Double.NaN, 0.1, -0.25), + val arr6: ShortArray = shortArrayOf(1, 2, 3), + val arr7: LongArray = longArrayOf(1, 2, 3), + val arr8: FloatArray = floatArrayOf(1.25f, 2.25f, 3.25f) + ) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is A) return false + + if (!arr.contentEquals(other.arr)) return false + if (!arr2.contentEquals(other.arr2)) return false + if (!arr3.contentEquals(other.arr3)) return false + if (!arr4.contentEquals(other.arr4)) return false + if (!arr5.contentEquals(other.arr5)) return false + if (!arr6.contentEquals(other.arr6)) return false + if (!arr7.contentEquals(other.arr7)) return false + if (!arr8.contentEquals(other.arr8)) return false + + return true + } + + override fun hashCode(): Int { + var result = arr.contentHashCode() + result = 31 * result + arr2.contentHashCode() + result = 31 * result + arr3.contentHashCode() + result = 31 * result + arr4.contentHashCode() + result = 31 * result + arr5.contentHashCode() + result = 31 * result + arr6.contentHashCode() + result = 31 * result + arr7.contentHashCode() + result = 31 * result + arr8.contentHashCode() + return result + } + + override fun toString(): String { + return "A(arr=${arr.contentToString()}, arr2=${arr2.contentToString()}, arr3=${arr3.contentToString()}, arr4=${arr4.contentToString()}, arr5=${arr5.contentToString()}, arr6=${arr6.contentToString()}, arr7=${arr7.contentToString()}, arr8=${arr8.contentToString()})" + } + } + + @Test + fun testCanBeSerialized() = assertStringFormAndRestored( + """{"arr":[1,2,3],"arr2":[1,2],"arr3":[true,false],"arr4":["a","b","c"],"arr5":[NaN,0.1,-0.25],"arr6":[1,2,3],"arr7":[1,2,3],"arr8":[1.25,2.25,3.25]}""", + A(byteArrayOf(1, 2, 3)), + A.serializer(), + lenient + ) +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/PropertyInitializerTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PropertyInitializerTest.kt new file mode 100644 index 00000000..f33069b8 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PropertyInitializerTest.kt @@ -0,0 +1,105 @@ +@file:Suppress("MayBeConstant") + +package kotlinx.serialization.features + +import kotlinx.serialization.* +import kotlinx.serialization.json.Json +import kotlin.test.Test +import kotlin.test.assertEquals + +internal val globalVar: Int = 4 + +internal fun globalFun(): Int { + return 7 +} + +internal const val PROPERTY_INITIALIZER_JSON = """{ + "valProperty": 1, + "varProperty": 2, + "literalConst": 3, + "globalVarRef": 4, + "computed": 5, + "doubleRef": 6, + "globalFun": 7, + "globalFunExpr": 8, + "itExpr": 9, + "transientRefFromProp": 10, + "bodyProp": 11, + "dependBodyProp": 12, + "getterDepend": 13 +}""" + +@Suppress("MemberVisibilityCanBePrivate", "unused", "ComplexRedundantLet") +class PropertyInitializerTest { + @Serializable + data class InternalClass( + val valProperty: Int, + var varProperty: Int, + val literalConst: Int = 3, + val globalVarRef: Int = globalVar, + val computed: Int = valProperty + varProperty + 2, + val doubleRef: Int = literalConst + literalConst, + var globalFun: Int = globalFun(), + var globalFunExpr: Int = globalFun() + 1, + val itExpr: Int = literalConst.let { it + 6 }, + @Transient val constTransient: Int = 6, + @Transient val serializedRefTransient: Int = varProperty + 1, + @Transient val refTransient: Int = serializedRefTransient, + val transientRefFromProp: Int = constTransient + 4, + ) { + val valGetter: Int get() { return 5 } + var bodyProp: Int = 11 + var dependBodyProp: Int = bodyProp + 1 + var getterDepend: Int = valGetter + 8 + } + + private val format = Json { encodeDefaults = true; prettyPrint = true } + + data class ExternalClass( + val valProperty: Int, + var varProperty: Int, + val literalConst: Int = 3, + val globalVarRef: Int = globalVar, + val computed: Int = valProperty + varProperty + 2, + val doubleRef: Int = literalConst + literalConst, + var globalFun: Int = globalFun(), + var globalFunExpr: Int = globalFun() + 1, + val itExpr: Int = literalConst.let { it + 6 }, + @Transient val constTransient: Int = 6, + @Transient val serializedRefTransient: Int = varProperty + 1, + @Transient val refTransient: Int = serializedRefTransient, + val transientRefFromProp: Int = constTransient + 4, + ) { + val valGetter: Int get() { return 5 } + var bodyProp: Int = 11 + var dependBodyProp: Int = bodyProp + 1 + var getterDepend: Int = valGetter + 8 + } + + @Serializer(ExternalClass::class) + object ExternalSerializer + + @Test + fun testInternalSerializeDefault() { + val encoded = format.encodeToString(InternalClass(1, 2)) + assertEquals(PROPERTY_INITIALIZER_JSON, encoded) + } + + @Test + fun testInternalDeserializeDefault() { + val decoded = format.decodeFromString<InternalClass>("""{"valProperty": 5, "varProperty": 6}""") + assertEquals(InternalClass(5, 6), decoded) + } + + @Test + fun testExternalSerializeDefault() { + val encoded = format.encodeToString(ExternalSerializer, ExternalClass(1, 2)) + assertEquals(PROPERTY_INITIALIZER_JSON, encoded) + } + + @Test + fun testExternalDeserializeDefault() { + val decoded = format.decodeFromString(ExternalSerializer,"""{"valProperty": 5, "varProperty": 6}""") + assertEquals(ExternalClass(5, 6), decoded) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/SealedClassesSerializationTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/SealedClassesSerializationTest.kt new file mode 100644 index 00000000..a99c2a1d --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/SealedClassesSerializationTest.kt @@ -0,0 +1,264 @@ +/* + * Copyright 2017-2021 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.builtins.* +import kotlinx.serialization.features.sealed.SealedChild +import kotlinx.serialization.features.sealed.SealedParent +import kotlinx.serialization.internal.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlin.test.* + +class SealedClassesSerializationTest : JsonTestBase() { + @Serializable + sealed class SealedProtocol { + @Serializable + @SerialName("SealedProtocol.StringMessage") + data class StringMessage(val description: String, val message: String) : SealedProtocol() + + @Serializable + @SerialName("SealedProtocol.IntMessage") + data class IntMessage(val description: String, val message: Int) : SealedProtocol() + + @Serializable + @SerialName("SealedProtocol.ErrorMessage") + data class ErrorMessage(val error: String) : SealedProtocol() + + @SerialName("EOF") + @Serializable + object EOF : SealedProtocol() + } + + @Serializable + sealed class ProtocolWithAbstractClass { + + @Serializable + @SerialName("ProtocolWithAbstractClass.Message") + abstract class Message : ProtocolWithAbstractClass() { + @Serializable + @SerialName("ProtocolWithAbstractClass.Message.StringMessage") + data class StringMessage(val description: String, val message: String) : Message() + + @Serializable + @SerialName("ProtocolWithAbstractClass.Message.IntMessage") + data class IntMessage(val description: String, val message: Int) : Message() + } + + @Serializable + @SerialName("ProtocolWithAbstractClass.ErrorMessage") + data class ErrorMessage(val error: String) : ProtocolWithAbstractClass() + + @SerialName("EOF") + @Serializable + object EOF : ProtocolWithAbstractClass() + } + + @Serializable + sealed class ProtocolWithSealedClass { + + @Serializable + @SerialName("ProtocolWithSealedClass.Message") + sealed class Message : ProtocolWithSealedClass() { + @Serializable + @SerialName("ProtocolWithSealedClass.Message.StringMessage") + data class StringMessage(val description: String, val message: String) : Message() + + @Serializable + @SerialName("ProtocolWithSealedClass.Message.IntMessage") + data class IntMessage(val description: String, val message: Int) : Message() + } + + @Serializable + @SerialName("ProtocolWithSealedClass.ErrorMessage") + data class ErrorMessage(val error: String) : ProtocolWithSealedClass() + + @SerialName("EOF") + @Serializable + object EOF : ProtocolWithSealedClass() + } + + @Serializable + sealed class ProtocolWithGenericClass { + + @Serializable + @SerialName("ProtocolWithGenericClass.Message") + data class Message<T>(val description: String, val message: T) : ProtocolWithGenericClass() + + @Serializable + @SerialName("ProtocolWithGenericClass.ErrorMessage") + data class ErrorMessage(val error: String) : ProtocolWithGenericClass() + + @SerialName("EOF") + @Serializable + object EOF : ProtocolWithGenericClass() + } + + private val ManualSerializer: KSerializer<SimpleSealed> = SealedClassSerializer( + "SimpleSealed", + SimpleSealed::class, + arrayOf(SimpleSealed.SubSealedA::class, SimpleSealed.SubSealedB::class), + arrayOf(SimpleSealed.SubSealedA.serializer(), SimpleSealed.SubSealedB.serializer()) + ) + + @Serializable + data class SealedHolder(val s: SimpleSealed) + + @Serializable + data class SealedBoxHolder(val b: Box<SimpleSealed>) + + private val arrayJson = Json { useArrayPolymorphism = true } + private val json = Json + + @Test + fun manualSerializer() { + val message = json.encodeToString( + ManualSerializer, + SimpleSealed.SubSealedB(42) + ) + assertEquals("{\"type\":\"kotlinx.serialization.SimpleSealed.SubSealedB\",\"i\":42}", message) + } + + @Test + fun onTopLevel() { + val arrayMessage = arrayJson.encodeToString( + SimpleSealed.serializer(), + SimpleSealed.SubSealedB(42) + ) + val message = json.encodeToString( + SimpleSealed.serializer(), + SimpleSealed.SubSealedB(42) + ) + assertEquals("{\"type\":\"kotlinx.serialization.SimpleSealed.SubSealedB\",\"i\":42}", message) + assertEquals("[\"kotlinx.serialization.SimpleSealed.SubSealedB\",{\"i\":42}]", arrayMessage) + } + + @Test + fun insideClass() { + assertJsonFormAndRestored( + SealedHolder.serializer(), + SealedHolder(SimpleSealed.SubSealedA("foo")), + """{"s":{"type":"kotlinx.serialization.SimpleSealed.SubSealedA","s":"foo"}}""", + json + ) + } + + @Test + fun insideGeneric() { + assertJsonFormAndRestored( + Box.serializer(SimpleSealed.serializer()), + Box<SimpleSealed>(SimpleSealed.SubSealedA("foo")), + """{"boxed":{"type":"kotlinx.serialization.SimpleSealed.SubSealedA","s":"foo"}}""", + json + ) + assertJsonFormAndRestored( + SealedBoxHolder.serializer(), + SealedBoxHolder(Box(SimpleSealed.SubSealedA("foo"))), + """{"b":{"boxed":{"type":"kotlinx.serialization.SimpleSealed.SubSealedA","s":"foo"}}}""", + json + ) + } + + @Test + fun complexProtocol() { + val messages = listOf<SealedProtocol>( + SealedProtocol.StringMessage("string message", "foo"), + SealedProtocol.IntMessage("int message", 42), + SealedProtocol.ErrorMessage("requesting termination"), + SealedProtocol.EOF + ) + val expected = + """[{"type":"SealedProtocol.StringMessage","description":"string message","message":"foo"},{"type":"SealedProtocol.IntMessage","description":"int message","message":42},{"type":"SealedProtocol.ErrorMessage","error":"requesting termination"},{"type":"EOF"}]""" + assertJsonFormAndRestored(ListSerializer(SealedProtocol.serializer()), messages, expected, json) + } + + @Test + fun protocolWithAbstractClass() { + val messages = listOf<ProtocolWithAbstractClass>( + ProtocolWithAbstractClass.Message.StringMessage("string message", "foo"), + ProtocolWithAbstractClass.Message.IntMessage("int message", 42), + ProtocolWithAbstractClass.ErrorMessage("requesting termination"), + ProtocolWithAbstractClass.EOF + ) + val abstractContext = SerializersModule { + + polymorphic(ProtocolWithAbstractClass::class) { + subclass(ProtocolWithAbstractClass.Message.IntMessage.serializer()) + subclass(ProtocolWithAbstractClass.Message.StringMessage.serializer()) + } + + polymorphic(ProtocolWithAbstractClass.Message::class) { + subclass(ProtocolWithAbstractClass.Message.IntMessage.serializer()) + subclass(ProtocolWithAbstractClass.Message.StringMessage.serializer()) + } + } + val json = Json { + serializersModule = abstractContext + } + val expected = + """[{"type":"ProtocolWithAbstractClass.Message.StringMessage","description":"string message","message":"foo"},{"type":"ProtocolWithAbstractClass.Message.IntMessage","description":"int message","message":42},{"type":"ProtocolWithAbstractClass.ErrorMessage","error":"requesting termination"},{"type":"EOF"}]""" + assertJsonFormAndRestored(ListSerializer(ProtocolWithAbstractClass.serializer()), messages, expected, json) + } + + @Test + fun protocolWithSealedClass() { + val messages = listOf<ProtocolWithSealedClass>( + ProtocolWithSealedClass.Message.StringMessage("string message", "foo"), + ProtocolWithSealedClass.Message.IntMessage("int message", 42), + ProtocolWithSealedClass.ErrorMessage("requesting termination"), + ProtocolWithSealedClass.EOF + ) + val expected = + """[{"type":"ProtocolWithSealedClass.Message.StringMessage","description":"string message","message":"foo"},{"type":"ProtocolWithSealedClass.Message.IntMessage","description":"int message","message":42},{"type":"ProtocolWithSealedClass.ErrorMessage","error":"requesting termination"},{"type":"EOF"}]""" + assertJsonFormAndRestored(ListSerializer(ProtocolWithSealedClass.serializer()), messages, expected, json) + } + + @Test + fun partOfProtocolWithSealedClass() { + val messages = listOf<ProtocolWithSealedClass.Message>( + ProtocolWithSealedClass.Message.StringMessage("string message", "foo"), + ProtocolWithSealedClass.Message.IntMessage("int message", 42) + ) + val expected = + """[{"type":"ProtocolWithSealedClass.Message.StringMessage","description":"string message","message":"foo"},{"type":"ProtocolWithSealedClass.Message.IntMessage","description":"int message","message":42}]""" + + assertJsonFormAndRestored(ListSerializer(ProtocolWithSealedClass.serializer()), messages, expected, json) + assertJsonFormAndRestored(ListSerializer(ProtocolWithSealedClass.Message.serializer()), messages, expected, json) + } + + @Test + fun protocolWithGenericClass() { + val messages = listOf<ProtocolWithGenericClass>( + ProtocolWithGenericClass.Message<String>("string message", "foo"), + ProtocolWithGenericClass.Message<Int>("int message", 42), + ProtocolWithGenericClass.ErrorMessage("requesting termination"), + ProtocolWithGenericClass.EOF + ) + val expected = + """[["ProtocolWithGenericClass.Message",{"description":"string message","message":["kotlin.String","foo"]}],["ProtocolWithGenericClass.Message",{"description":"int message","message":["kotlin.Int",42]}],["ProtocolWithGenericClass.ErrorMessage",{"error":"requesting termination"}],["EOF",{}]]""" + val json = Json { + useArrayPolymorphism = true + serializersModule = SerializersModule { + polymorphic(Any::class) { + subclass(Int::class) + subclass(String::class) + } + } + } + assertJsonFormAndRestored(ListSerializer(ProtocolWithGenericClass.serializer()), messages, expected, json) + } + + @Test + fun testSerializerLookupForSealedClass() { + val resSer = serializer<SealedProtocol>() + assertEquals(SealedProtocol::class, (resSer as AbstractPolymorphicSerializer).baseClass) + } + + @Test + fun testClassesFromDifferentFiles() { + assertJsonFormAndRestored(SealedParent.serializer(), SealedChild(5), """{"type":"first child","i":1,"j":5}""") + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/SealedPolymorphismTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/SealedPolymorphismTest.kt new file mode 100644 index 00000000..4cc289a9 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/SealedPolymorphismTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2017-2019 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.Json +import kotlinx.serialization.modules.* +import kotlinx.serialization.test.assertStringFormAndRestored +import kotlin.test.Test + +class SealedPolymorphismTest { + + @Serializable + data class FooHolder( + val someMetadata: Int, + val payload: List<@Polymorphic Foo> + ) + + @Serializable + @SerialName("Foo") + sealed class Foo { + @Serializable + @SerialName("Bar") + data class Bar(val bar: Int) : Foo() + @Serializable + @SerialName("Baz") + data class Baz(val baz: String) : Foo() + } + + val sealedModule = SerializersModule { + polymorphic(Foo::class) { + subclass(Foo.Bar.serializer()) + subclass(Foo.Baz.serializer()) + } + } + + val json = Json { serializersModule = sealedModule } + + @Test + fun testSaveSealedClassesList() { + assertStringFormAndRestored( + """{"someMetadata":42,"payload":[ + |{"type":"Bar","bar":1}, + |{"type":"Baz","baz":"2"}]}""".trimMargin().replace("\n", ""), + FooHolder(42, listOf(Foo.Bar(1), Foo.Baz("2"))), + FooHolder.serializer(), + json, + printResult = true + ) + } + + @Test + fun testCanSerializeSealedClassPolymorphicallyOnTopLevel() { + assertStringFormAndRestored( + """{"type":"Bar","bar":1}""", + Foo.Bar(1), + PolymorphicSerializer(Foo::class), + json + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/SerializableOnTypeUsageTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/SerializableOnTypeUsageTest.kt new file mode 100644 index 00000000..e991a0db --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/SerializableOnTypeUsageTest.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.features + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import kotlin.test.Test +import kotlin.test.assertEquals + + +@Serializable +data class SerializableOnArguments( + val list1: List<@Serializable(MultiplyingIntSerializer::class) Int>, + val list2: List<List<@Serializable(MultiplyingIntHolderSerializer::class) IntHolder>> +) + +class SerializableOnTypeUsageTest { + @Test + fun testAnnotationIsApplied() { + val data = SerializableOnArguments(listOf(1, 2), listOf(listOf(IntHolder(42)))) + val str = Json.encodeToString(SerializableOnArguments.serializer(), data) + assertEquals("""{"list1":[2,4],"list2":[[84]]}""", str) + val restored = Json.decodeFromString(SerializableOnArguments.serializer(), str) + assertEquals(data, restored) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/SerializableWithTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/SerializableWithTest.kt new file mode 100644 index 00000000..a9cf9638 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/SerializableWithTest.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2017-2020 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.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.Json +import kotlin.test.Test +import kotlin.test.assertEquals + +object MultiplyingIntSerializer : KSerializer<Int> { + override val descriptor: SerialDescriptor + get() = PrimitiveSerialDescriptor("MultiplyingInt", PrimitiveKind.INT) + + override fun deserialize(decoder: Decoder): Int { + return decoder.decodeInt() / 2 + } + + override fun serialize(encoder: Encoder, value: Int) { + encoder.encodeInt(value * 2) + } +} + +object DividingIntSerializer : KSerializer<Int> { + override val descriptor: SerialDescriptor + get() = PrimitiveSerialDescriptor("DividedInt", PrimitiveKind.INT) + + override fun deserialize(decoder: Decoder): Int { + return decoder.decodeInt() * 2 + } + + override fun serialize(encoder: Encoder, value: Int) { + encoder.encodeInt(value / 2) + } +} + +@Serializable(with = DividingIntHolderSerializer::class) +data class IntHolder(val data: Int) + +@Serializer(IntHolder::class) +object MultiplyingIntHolderSerializer { + override fun deserialize(decoder: Decoder): IntHolder { + return IntHolder(decoder.decodeInt() / 2) + } + + override fun serialize(encoder: Encoder, value: IntHolder) { + encoder.encodeInt(value.data * 2) + } +} + +@Serializer(IntHolder::class) +object DividingIntHolderSerializer { + override fun deserialize(decoder: Decoder): IntHolder { + return IntHolder(decoder.decodeInt() * 2) + } + + override fun serialize(encoder: Encoder, value: IntHolder) { + encoder.encodeInt(value.data / 2) + } +} + +@Serializable +data class Carrier( + @Serializable(with = MultiplyingIntHolderSerializer::class) val a: IntHolder, + @Serializable(with = MultiplyingIntSerializer::class) val i: Int +) + +class SerializableWithTest { + @Test + fun testOnProperties() { + val str = Json.encodeToString(Carrier.serializer(), Carrier(IntHolder(42), 2)) + assertEquals("""{"a":84,"i":4}""", str) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/SkipDefaults.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/SkipDefaults.kt new file mode 100644 index 00000000..a7fea75f --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/SkipDefaults.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2017-2020 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.EncodeDefault.Mode.* +import kotlinx.serialization.json.* +import kotlin.test.* + +class SkipDefaultsTest { + private val jsonDropDefaults = Json { encodeDefaults = false } + private val jsonEncodeDefaults = Json { encodeDefaults = true } + + @Serializable + data class Data(val bar: String, val foo: Int = 42) { + var list: List<Int> = emptyList() + val listWithSomething: List<Int> = listOf(1, 2, 3) + } + + @Serializable + data class DifferentModes( + val a: String = "a", + @EncodeDefault val b: String = "b", + @EncodeDefault(ALWAYS) val c: String = "c", + @EncodeDefault(NEVER) val d: String = "d" + ) + + @Test + fun serializeCorrectlyDefaults() { + val d = Data("bar") + assertEquals( + """{"bar":"bar","foo":42,"list":[],"listWithSomething":[1,2,3]}""", + jsonEncodeDefaults.encodeToString(Data.serializer(), d) + ) + } + + @Test + fun serializeCorrectly() { + val d = Data("bar", 100).apply { list = listOf(1, 2, 3) } + assertEquals( + """{"bar":"bar","foo":100,"list":[1,2,3]}""", + jsonDropDefaults.encodeToString(Data.serializer(), d) + ) + } + + @Test + fun serializeCorrectlyAndDropBody() { + val d = Data("bar", 43) + assertEquals("""{"bar":"bar","foo":43}""", jsonDropDefaults.encodeToString(Data.serializer(), d)) + } + + @Test + fun serializeCorrectlyAndDropAll() { + val d = Data("bar") + assertEquals("""{"bar":"bar"}""", jsonDropDefaults.encodeToString(Data.serializer(), d)) + } + + @Test + fun encodeDefaultsAnnotationWithFlag() { + val data = DifferentModes() + assertEquals("""{"a":"a","b":"b","c":"c"}""", jsonEncodeDefaults.encodeToString(data)) + assertEquals("""{"b":"b","c":"c"}""", jsonDropDefaults.encodeToString(data)) + } + +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/UseSerializersTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/UseSerializersTest.kt new file mode 100644 index 00000000..b5f332d7 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/UseSerializersTest.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:UseSerializers(MultiplyingIntHolderSerializer::class, MultiplyingIntSerializer::class) + +package kotlinx.serialization.features + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlin.test.* + +@Serializable +data class Carrier2( + val a: IntHolder, + val i: Int, + val nullable: Int?, + val nullableIntHolder: IntHolder?, + val nullableIntList: List<Int?> = emptyList(), + val nullableIntHolderNullableList: List<IntHolder?>? = null +) + +class UseSerializersTest { + @Test + fun testOnFile() { + val str = Json { encodeDefaults = true }.encodeToString( + Carrier2.serializer(), + Carrier2(IntHolder(42), 2, 2, IntHolder(42)) + ) + assertEquals("""{"a":84,"i":4,"nullable":4,"nullableIntHolder":84,"nullableIntList":[],"nullableIntHolderNullableList":null}""", str) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/EncodeInlineElementTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/EncodeInlineElementTest.kt new file mode 100644 index 00000000..aa4866f4 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/EncodeInlineElementTest.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.features.inline + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.test.* +import kotlin.test.* + +@Serializable(WithUnsignedSerializer::class) +data class WithUnsigned(val u: UInt) + +object WithUnsignedSerializer : KSerializer<WithUnsigned> { + override fun serialize(encoder: Encoder, value: WithUnsigned) { + val ce = encoder.beginStructure(descriptor) + ce.encodeInlineElement(descriptor, 0).encodeInt(value.u.toInt()) + ce.endStructure(descriptor) + } + + override fun deserialize(decoder: Decoder): WithUnsigned { + val cd = decoder.beginStructure(descriptor) + var u: UInt = 0.toUInt() + loop@ while (true) { + u = when (val i = cd.decodeElementIndex(descriptor)) { + 0 -> cd.decodeInlineElement(descriptor, i).decodeInt().toUInt() + else -> break@loop + } + } + cd.endStructure(descriptor) + return WithUnsigned(u) + } + + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("WithUnsigned") { + element("u", UInt.serializer().descriptor) + } +} + +class EncodeInlineElementTest { + @Test + fun wrapper() { + val w = WithUnsigned(Int.MAX_VALUE.toUInt() + 1.toUInt()) + assertStringFormAndRestored<WithUnsigned>("""{"u":2147483648}""", w, WithUnsignedSerializer, printResult = true) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/InlineClassesCompleteTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/InlineClassesCompleteTest.kt new file mode 100644 index 00000000..96972f92 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/InlineClassesCompleteTest.kt @@ -0,0 +1,135 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +@file:Suppress("INLINE_CLASSES_NOT_SUPPORTED", "SERIALIZER_NOT_FOUND") + +package kotlinx.serialization.features.inline + +import kotlinx.serialization.* +import kotlinx.serialization.Box +import kotlinx.serialization.test.* +import kotlin.jvm.* +import kotlin.test.* + +@Serializable +@JvmInline +value class MyInt(val i: Int) + +@Serializable +@JvmInline value class NullableMyInt(val i: Int?) + +@Serializable +@JvmInline value class OverSerializable(val s: IntData) + +@Serializable +@JvmInline value class OverSerializableNullable(val s: IntData?) + +@Serializable +@JvmInline value class WithT<T>(val t: Box<T>) + +@Serializable +@JvmInline value class WithTNullable<T>(val t: Box<T>?) + +@Serializable +data class WithAll( + val myInt: MyInt, // I + val myIntNullable: MyInt?, // LMyInt; + val nullableMyInt: NullableMyInt, //Ljava/lang/Integer; + val nullableMyIntNullable: NullableMyInt?, // LNullableMyInt + val overSerializable: OverSerializable, // LIntData; + val overSerializableNullable: OverSerializable?, // LIntData; + val nullableOverSerializable: OverSerializableNullable, // LIntData; + val nullableOverSerializableNullable: OverSerializableNullable?, // LOverSerializableNullable; + val withT: WithT<Int>, // LBox; + val withTNullable: WithT<Int>?, // LBox + val withNullableTNullable: WithT<Int?>?, // LBox; + val withTNullableTNullable: WithTNullable<Int?>? // LWithTNullable; +) + +@Serializable +data class WithGenerics( + val myInt: Box<MyInt>, + val myIntNullable: Box<MyInt?>, + val nullableMyInt: Box<NullableMyInt>, + val nullableMyIntNullable: Box<NullableMyInt?>, + val overSerializable: Box<OverSerializable>, + val overSerializableNullable: Box<OverSerializable?>, + val nullableOverSerializable: Box<OverSerializableNullable>, + val nullableOverSerializableNullable: Box<OverSerializableNullable?>, + val boxInBox: Box<WithT<Int>> +) + +class InlineClassesCompleteTest { + @Test + fun testAllVariantsWithoutNull() { + val withAll = WithAll( + MyInt(1), + MyInt(2), + NullableMyInt(3), + NullableMyInt(4), + OverSerializable(IntData(5)), + OverSerializable(IntData(6)), + OverSerializableNullable(IntData(7)), + OverSerializableNullable(IntData(8)), + WithT(Box(9)), + WithT(Box(10)), + WithT(Box(11)), + WithTNullable(Box(12)) + ) + assertSerializedAndRestored(withAll, WithAll.serializer()) + } + + @Test + fun testAllVariantsWithNull() { + assertSerializedAndRestored( + WithAll( + MyInt(1), + null, + NullableMyInt(null), + null, + OverSerializable(IntData(5)), + null, + OverSerializableNullable(null), + null, + WithT(Box(9)), + null, + WithT(Box(null)), + WithTNullable(Box(null)) + ), WithAll.serializer() + ) + } + + @Test + fun testAllGenericVariantsWithoutNull() { + assertSerializedAndRestored( + WithGenerics( + Box(MyInt(1)), + Box(MyInt(2)), + Box(NullableMyInt(3)), + Box(NullableMyInt(4)), + Box(OverSerializable(IntData(5))), + Box(OverSerializable(IntData(6))), + Box(OverSerializableNullable(IntData(7))), + Box(OverSerializableNullable(IntData(8))), + Box(WithT(Box(9))) + ), WithGenerics.serializer() + ) + } + + @Test + fun testAllGenericVariantsWithNull() { + assertSerializedAndRestored( + WithGenerics( + Box(MyInt(1)), + Box(null), + Box(NullableMyInt(null)), + Box(null), + Box(OverSerializable(IntData(5))), + Box(null), + Box(OverSerializableNullable(null)), + Box(null), + Box(WithT(Box(9))) + ), WithGenerics.serializer() + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/InlineClassesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/InlineClassesTest.kt new file mode 100644 index 00000000..f3eb9511 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/InlineClassesTest.kt @@ -0,0 +1,216 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:Suppress("INLINE_CLASSES_NOT_SUPPORTED", "SERIALIZER_NOT_FOUND") + +package kotlinx.serialization.features.inline + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlinx.serialization.test.* +import kotlin.jvm.* +import kotlin.test.* + +@Serializable +data class SimpleContainerForUInt(val i: UInt) + +@Serializable(MyUIntSerializer::class) +@JvmInline +value class MyUInt(val m: Int) + +object MyUIntSerializer : KSerializer<MyUInt> { + override val descriptor = UInt.serializer().descriptor + override fun serialize(encoder: Encoder, value: MyUInt) { + encoder.encodeInline(descriptor).encodeInt(value.m) + } + + override fun deserialize(decoder: Decoder): MyUInt { + return MyUInt(decoder.decodeInline(descriptor).decodeInt()) + } +} + +@Serializable +data class SimpleContainerForMyType(val i: MyUInt) + +@Serializable +@JvmInline +value class MyList<T>(val list: List<T>) + +@Serializable +data class ContainerForList<T>(val i: MyList<T>) + +@Serializable +data class UnsignedInBoxedPosition(val i: List<UInt>) + +@Serializable +data class MixedPositions( + val int: Int, + val intNullable: Int?, + val uint: UInt, + val uintNullable: UInt?, + val boxedInt: List<Int>, + val boxedUInt: List<UInt>, + val boxedNullableInt: List<Int?>, + val boxedNullableUInt: List<UInt?> +) + +@Serializable +@JvmInline +value class ResourceId(val id: String) + +@Serializable +@JvmInline +value class ResourceType(val type: String) + +@Serializable +@JvmInline +value class ResourceKind(val kind: SampleEnum) + +@Serializable +data class ResourceIdentifier(val id: ResourceId, val type: ResourceType, val type2: ValueWrapper) + +@Serializable +@JvmInline +value class ValueWrapper(val wrapped: ResourceType) + +@Serializable +@JvmInline +value class Outer(val inner: Inner) + +@Serializable +data class Inner(val n: Int) + +@Serializable +data class OuterOuter(val outer: Outer) + +@Serializable +@JvmInline +value class WithList(val value: List<Int>) + +class InlineClassesTest : JsonTestBase() { + private val precedent: UInt = Int.MAX_VALUE.toUInt() + 10.toUInt() + + @Test + fun withList() { + val withList = WithList(listOf(1, 2, 3)) + assertJsonFormAndRestored(WithList.serializer(), withList, """[1,2,3]""") + } + + @Test + fun testOuterInner() { + val o = Outer(Inner(10)) + assertJsonFormAndRestored(Outer.serializer(), o, """{"n":10}""") + } + + @Test + fun testOuterOuterInner() { + val o = OuterOuter(Outer(Inner(10))) + assertJsonFormAndRestored(OuterOuter.serializer(), o, """{"outer":{"n":10}}""") + } + + @Test + fun testTopLevel() { + assertJsonFormAndRestored( + ResourceType.serializer(), + ResourceType("foo"), + """"foo"""", + ) + } + + @Test + fun testTopLevelOverEnum() { + assertJsonFormAndRestored( + ResourceKind.serializer(), + ResourceKind(SampleEnum.OptionC), + """"OptionC"""", + ) + } + + @Test + fun testTopLevelWrapper() { + assertJsonFormAndRestored( + ValueWrapper.serializer(), + ValueWrapper(ResourceType("foo")), + """"foo"""", + ) + } + + @Test + fun testTopLevelContextual() { + val module = SerializersModule { + contextual<ResourceType>(ResourceType.serializer()) + } + val json = Json(default) { serializersModule = module } + assertJsonFormAndRestored( + ContextualSerializer(ResourceType::class), + ResourceType("foo"), + """"foo"""", + json + ) + } + + + @Test + fun testSimpleContainer() { + assertJsonFormAndRestored( + SimpleContainerForUInt.serializer(), + SimpleContainerForUInt(precedent), + """{"i":2147483657}""", + ) + } + + @Test + fun testSimpleContainerForMyTypeWithCustomSerializer() = assertJsonFormAndRestored( + SimpleContainerForMyType.serializer(), + SimpleContainerForMyType(MyUInt(precedent.toInt())), + """{"i":2147483657}""", + ) + + @Test + fun testSimpleContainerForList() { + assertJsonFormAndRestored( + ContainerForList.serializer(UInt.serializer()), + ContainerForList(MyList(listOf(precedent))), + """{"i":[2147483657]}""", + ) + } + + @Test + fun testInlineClassesWithStrings() { + assertJsonFormAndRestored( + ResourceIdentifier.serializer(), + ResourceIdentifier(ResourceId("resId"), ResourceType("resType"), ValueWrapper(ResourceType("wrappedType"))), + """{"id":"resId","type":"resType","type2":"wrappedType"}""" + ) + } + + @Test + fun testUnsignedInBoxedPosition() = assertJsonFormAndRestored( + UnsignedInBoxedPosition.serializer(), + UnsignedInBoxedPosition(listOf(precedent)), + """{"i":[2147483657]}""", + ) + + @Test + fun testMixedPositions() { + val o = MixedPositions( + int = precedent.toInt(), + intNullable = precedent.toInt(), + uint = precedent, + uintNullable = precedent, + boxedInt = listOf(precedent.toInt()), + boxedUInt = listOf(precedent), + boxedNullableInt = listOf(null, precedent.toInt(), null), + boxedNullableUInt = listOf(null, precedent, null) + ) + assertJsonFormAndRestored( + MixedPositions.serializer(), + o, + """{"int":-2147483639,"intNullable":-2147483639,"uint":2147483657,"uintNullable":2147483657,"boxedInt":[-2147483639],"boxedUInt":[2147483657],"boxedNullableInt":[null,-2147483639,null],"boxedNullableUInt":[null,2147483657,null]}""", + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/InlineMapQuotedTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/InlineMapQuotedTest.kt new file mode 100644 index 00000000..63157d12 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/InlineMapQuotedTest.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.features.inline + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlinx.serialization.test.* +import kotlin.jvm.* +import kotlin.test.* + +class InlineMapQuotedTest : JsonTestBase() { + @Serializable(with = CustomULong.Serializer::class) + data class CustomULong(val value: ULong) { + @OptIn(ExperimentalSerializationApi::class, ExperimentalUnsignedTypes::class) + internal object Serializer : KSerializer<CustomULong> { + override val descriptor: SerialDescriptor = + @OptIn(ExperimentalUnsignedTypes::class) ULong.serializer().descriptor + + override fun deserialize(decoder: Decoder): CustomULong = + CustomULong(decoder.decodeInline(descriptor).decodeSerializableValue(ULong.serializer())) + + override fun serialize(encoder: Encoder, value: CustomULong) { + encoder.encodeInline(descriptor).encodeSerializableValue(ULong.serializer(), value.value) + } + } + } + + @JvmInline + @Serializable + value class WrappedLong(val value: Long) + + @JvmInline + @Serializable + value class WrappedULong(val value: ULong) + + @Serializable + data class Carrier( + val mapLong: Map<Long, Long>, + val mapULong: Map<ULong, Long>, + val wrappedLong: Map<WrappedLong, Long>, + val mapWrappedU: Map<WrappedULong, Long>, + val mapCustom: Map<CustomULong, Long> + ) + + @Test + fun testInlineClassAsMapKey() { + println(Long.MAX_VALUE.toULong() + 2UL) + val c = Carrier( + mapOf(1L to 1L), + mapOf(Long.MAX_VALUE.toULong() + 2UL to 2L), + mapOf(WrappedLong(3L) to 3L), + mapOf(WrappedULong(Long.MAX_VALUE.toULong() + 4UL) to 4L), + mapOf(CustomULong(Long.MAX_VALUE.toULong() + 5UL) to 5L) + ) + assertJsonFormAndRestored( + serializer<Carrier>(), + c, + """{"mapLong":{"1":1},"mapULong":{"9223372036854775809":2},"wrappedLong":{"3":3},"mapWrappedU":{"9223372036854775811":4},"mapCustom":{"9223372036854775812":5}}""" + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/UnsignedIntegersTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/UnsignedIntegersTest.kt new file mode 100644 index 00000000..5e24c7fa --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/UnsignedIntegersTest.kt @@ -0,0 +1,140 @@ +/* + * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.features.inline + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlinx.serialization.json.* +import kotlin.test.* + +class UnsignedIntegersTest : JsonTestBase() { + @Serializable + data class AllUnsigned( + val uInt: UInt, + val uLong: ULong, + val uByte: UByte, + val uShort: UShort, + val signedInt: Int, + val signedLong: Long, + val double: Double + ) + + @ExperimentalUnsignedTypes + @Serializable + data class UnsignedArrays( + val uByte: UByteArray, + val uShort: UShortArray, + val uInt: UIntArray, + val uLong: ULongArray + ) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as UnsignedArrays + + if (!uByte.contentEquals(other.uByte)) return false + if (!uShort.contentEquals(other.uShort)) return false + if (!uInt.contentEquals(other.uInt)) return false + if (!uLong.contentEquals(other.uLong)) return false + + return true + } + + override fun hashCode(): Int { + var result = uByte.contentHashCode() + result = 31 * result + uShort.contentHashCode() + result = 31 * result + uInt.contentHashCode() + result = 31 * result + uLong.contentHashCode() + return result + } + } + + @Serializable + data class UnsignedWithoutLong(val uInt: UInt, val uByte: UByte, val uShort: UShort) + + @Test + fun testUnsignedIntegersJson() { + val data = AllUnsigned( + Int.MAX_VALUE.toUInt() + 10.toUInt(), + Long.MAX_VALUE.toULong() + 10.toULong(), + 239.toUByte(), + 65000.toUShort(), + -42, + Long.MIN_VALUE, + 1.1 + ) + assertJsonFormAndRestored( + AllUnsigned.serializer(), + data, + """{"uInt":2147483657,"uLong":9223372036854775817,"uByte":239,"uShort":65000,"signedInt":-42,"signedLong":-9223372036854775808,"double":1.1}""", + ) + } + + @Test + fun testUnsignedIntegersWithoutLongJson() { + val data = UnsignedWithoutLong( + Int.MAX_VALUE.toUInt() + 10.toUInt(), + 239.toUByte(), + 65000.toUShort(), + ) + assertJsonFormAndRestored( + UnsignedWithoutLong.serializer(), + data, + """{"uInt":2147483657,"uByte":239,"uShort":65000}""", + ) + } + + @Test + fun testRoot() { + assertJsonFormAndRestored(UByte.serializer(), 220U, "220") + assertJsonFormAndRestored(UShort.serializer(), 65000U, "65000") + assertJsonFormAndRestored(UInt.serializer(), 2147483657U, "2147483657") + assertJsonFormAndRestored(ULong.serializer(), 9223372036854775817U, "9223372036854775817") + } + + @OptIn(ExperimentalUnsignedTypes::class) + @Test + fun testRootArrays() = parametrizedTest { + assertJsonFormAndRestoredCustom( + UByteArraySerializer(), + ubyteArrayOf(1U, 220U), + "[1,220]" + ) { l, r -> l.contentEquals(r) } + + assertJsonFormAndRestoredCustom( + UShortArraySerializer(), + ushortArrayOf(1U, 65000U), + "[1,65000]" + ) { l, r -> l.contentEquals(r) } + + assertJsonFormAndRestoredCustom( + UIntArraySerializer(), + uintArrayOf(1U, 2147483657U), + "[1,2147483657]" + ) { l, r -> l.contentEquals(r) } + + assertJsonFormAndRestoredCustom( + ULongArraySerializer(), + ulongArrayOf(1U, 9223372036854775817U), + "[1,9223372036854775817]" + ) { l, r -> l.contentEquals(r) } + } + + @OptIn(ExperimentalUnsignedTypes::class) + @Test + fun testArrays() { + val data = UnsignedArrays( + ubyteArrayOf(1U, 220U), + ushortArrayOf(1U, 65000U), + uintArrayOf(1U, 2147483657U), + ulongArrayOf(1U, 9223372036854775817U) + ) + val json = """{"uByte":[1,220],"uShort":[1,65000],"uInt":[1,2147483657],"uLong":[1,9223372036854775817]}""" + + assertJsonFormAndRestored(UnsignedArrays.serializer(), data, json) + } + +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/ValueClassesInSealedHierarchyTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/ValueClassesInSealedHierarchyTest.kt new file mode 100644 index 00000000..ed968298 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/inline/ValueClassesInSealedHierarchyTest.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.features.inline + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.test.* +import kotlin.jvm.* +import kotlin.test.* + +class ValueClassesInSealedHierarchyTest : JsonTestBase() { + @Test + fun testSingle() { + val single = "foo" + assertJsonFormAndRestored( + AnyValue.serializer(), + AnyValue.Single(single), + "\"$single\"" + ) + } + + @Test + fun testComplex() { + val complexJson = """{"id":"1","name":"object"}""" + assertJsonFormAndRestored( + AnyValue.serializer(), + AnyValue.Complex(mapOf("id" to "1", "name" to "object")), + complexJson + ) + } + + @Test + fun testMulti() { + val multiJson = """["list","of","strings"]""" + assertJsonFormAndRestored( + AnyValue.serializer(), + AnyValue.Multi(listOf("list", "of", "strings")), + multiJson + ) + } +} + + +// From https://github.com/Kotlin/kotlinx.serialization/issues/2159 +@Serializable(with = AnyValue.Companion.Serializer::class) +sealed interface AnyValue { + + @JvmInline + @Serializable + value class Single(val value: String) : AnyValue + + @JvmInline + @Serializable + value class Multi(val values: List<String>) : AnyValue + + @JvmInline + @Serializable + value class Complex(val values: Map<String, String>) : AnyValue + + @JvmInline + @Serializable + value class Unknown(val value: JsonElement) : AnyValue + + companion object { + object Serializer : JsonContentPolymorphicSerializer<AnyValue>(AnyValue::class) { + + override fun selectDeserializer(element: JsonElement): DeserializationStrategy<AnyValue> = + when { + element is JsonArray && element.all { it is JsonPrimitive && it.isString } -> Multi.serializer() + element is JsonObject && element.values.all { it is JsonPrimitive && it.isString } -> Complex.serializer() + element is JsonPrimitive && element.isString -> Single.serializer() + else -> Unknown.serializer() + } + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedChild.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedChild.kt new file mode 100644 index 00000000..1bb2b02b --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedChild.kt @@ -0,0 +1,8 @@ +package kotlinx.serialization.features.sealed + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +@SerialName("first child") +data class SealedChild(val j: Int) : SealedParent(1) diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedDiamondTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedDiamondTest.kt new file mode 100644 index 00000000..6ba4713b --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedDiamondTest.kt @@ -0,0 +1,49 @@ +package kotlinx.serialization.features.sealed + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.json.* +import kotlin.test.* + +class SealedDiamondTest : JsonTestBase() { + + @Serializable + sealed interface A {} + + @Serializable + sealed interface B : A {} + + @Serializable + sealed interface C : A {} + + @Serializable + @SerialName("X") + data class X(val i: Int) : B, C + + @Serializable + @SerialName("Y") + object Y : B, C + + @SerialName("E") + enum class E : B, C { + Q, W + } + + @Test + fun testMultipleSuperSealedInterfacesDescriptor() { + val subclasses = A.serializer().descriptor.getElementDescriptor(1).elementDescriptors.map { it.serialName } + assertEquals(listOf("E", "X", "Y"), subclasses) + } + + @Test + fun testMultipleSuperSealedInterfaces() { + @Serializable + data class Carrier(val a: A, val b: B, val c: C) + assertJsonFormAndRestored( + Carrier.serializer(), + Carrier(X(1), X(2), Y), + """{"a":{"type":"X","i":1},"b":{"type":"X","i":2},"c":{"type":"Y"}}""" + ) + } + +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedInterfacesJsonSerializationTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedInterfacesJsonSerializationTest.kt new file mode 100644 index 00000000..a2e6bb67 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedInterfacesJsonSerializationTest.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.features.sealed + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class SealedInterfacesJsonSerializationTest : JsonTestBase() { + @Serializable + sealed interface I + + @Serializable + sealed class Response: I { + @Serializable + @SerialName("ResponseInt") + data class ResponseInt(val i: Int): Response() + + @Serializable + @SerialName("ResponseString") + data class ResponseString(val s: String): Response() + } + + @Serializable + @SerialName("NoResponse") + object NoResponse: I + + @Test + fun testSealedInterfaceJson() { + val messages = listOf(Response.ResponseInt(10), NoResponse, Response.ResponseString("foo")) + assertJsonFormAndRestored( + serializer(), + messages, + """[{"type":"ResponseInt","i":10},{"type":"NoResponse"},{"type":"ResponseString","s":"foo"}]""" + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedParent.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedParent.kt new file mode 100644 index 00000000..b096c120 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedParent.kt @@ -0,0 +1,6 @@ +package kotlinx.serialization.features.sealed + +import kotlinx.serialization.Serializable + +@Serializable +sealed class SealedParent(val i: Int) diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/AbstractJsonImplicitNullsTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/AbstractJsonImplicitNullsTest.kt new file mode 100644 index 00000000..a2f4a9df --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/AbstractJsonImplicitNullsTest.kt @@ -0,0 +1,151 @@ +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlin.test.* + +/* + * Actual testing should be performed in subclasses. + * Subclasses implement serialization and deserialization for different types: serial Kotlin classes, JsonElement, dynamic etc + */ +@Ignore +abstract class AbstractJsonImplicitNullsTest { + @Serializable + data class Nullable( + val f0: Int?, + val f1: Int?, + val f2: Int?, + val f3: Int?, + ) + + @Serializable + data class WithNotNull( + val f0: Int?, + val f1: Int?, + val f2: Int, + ) + + @Serializable + data class WithOptional( + val f0: Int?, + val f1: Int? = 1, + val f2: Int = 2, + ) + + @Serializable + data class Outer(val i: Inner) + + @Serializable + data class Inner(val s1: String?, val s2: String?) + + @Serializable + data class ListWithNullable(val l: List<Int?>) + + @Serializable + data class MapWithNullable(val m: Map<Int?, Int?>) + + @Serializable + data class NullableList(val l: List<Int>?) + + @Serializable + data class NullableMap(val m: Map<Int, Int>?) + + + private val format = Json { explicitNulls = false } + + protected abstract fun <T> Json.encode(value: T, serializer: KSerializer<T>): String + + protected abstract fun <T> Json.decode(json: String, serializer: KSerializer<T>): T + + @Test + fun testExplicit() { + val plain = Nullable(null, 10, null, null) + val json = """{"f0":null,"f1":10,"f2":null,"f3":null}""" + + assertEquals(json, Json.encode(plain, Nullable.serializer())) + assertEquals(plain, Json.decode(json, Nullable.serializer())) + } + + @Test + fun testNullable() { + val plain = Nullable(null, 10, null, null) + val json = """{"f1":10}""" + + assertEquals(json, format.encode(plain, Nullable.serializer())) + assertEquals(plain, format.decode(json, Nullable.serializer())) + } + + @Test + fun testMissingNotNull() { + val json = """{"f1":10}""" + + assertFailsWith(SerializationException::class) { + format.decode(json, WithNotNull.serializer()) + } + } + + @Test + fun testDecodeOptional() { + val json = """{}""" + + val decoded = format.decode(json, WithOptional.serializer()) + assertEquals(WithOptional(null), decoded) + } + + + @Test + fun testNestedJsonObject() { + val json = """{"i": {}}""" + + val decoded = format.decode(json, Outer.serializer()) + assertEquals(Outer(Inner(null, null)), decoded) + } + + @Test + fun testListWithNullable() { + val jsonWithNull = """{"l":[null]}""" + val jsonWithEmptyList = """{"l":[]}""" + + val encoded = format.encode(ListWithNullable(listOf(null)), ListWithNullable.serializer()) + assertEquals(jsonWithNull, encoded) + + val decoded = format.decode(jsonWithEmptyList, ListWithNullable.serializer()) + assertEquals(ListWithNullable(emptyList()), decoded) + } + + @Test + fun testMapWithNullable() { + val jsonWithNull = """{"m":{null:null}}""" + val jsonWithQuotedNull = """{"m":{"null":null}}""" + val jsonWithEmptyList = """{"m":{}}""" + + val encoded = format.encode(MapWithNullable(mapOf(null to null)), MapWithNullable.serializer()) + //Json encode map null key as `null:` but other external utilities may encode it as a String `"null":` + assertTrue { listOf(jsonWithNull, jsonWithQuotedNull).contains(encoded) } + + val decoded = format.decode(jsonWithEmptyList, MapWithNullable.serializer()) + assertEquals(MapWithNullable(emptyMap()), decoded) + } + + @Test + fun testNullableList() { + val json = """{}""" + + val encoded = format.encode(NullableList(null), NullableList.serializer()) + assertEquals(json, encoded) + + val decoded = format.decode(json, NullableList.serializer()) + assertEquals(NullableList(null), decoded) + } + + @Test + fun testNullableMap() { + val json = """{}""" + + val encoded = format.encode(NullableMap(null), NullableMap.serializer()) + assertEquals(json, encoded) + + val decoded = format.decode(json, NullableMap.serializer()) + assertEquals(NullableMap(null), decoded) + } + +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/BasicTypesSerializationTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/BasicTypesSerializationTest.kt new file mode 100644 index 00000000..4959b7e2 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/BasicTypesSerializationTest.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class BasicTypesSerializationTest : JsonTestBase() { + + val goldenValue = """ + {"unit":{},"boolean":true,"byte":10,"short":20,"int":30,"long":40,"float":50.1,"double":60.1,"char":"A","string":"Str0","enum":"POSITIVE","intData":{"intV":70},"unitN":null,"booleanN":null,"byteN":11,"shortN":21,"intN":31,"longN":41,"floatN":51.1,"doubleN":61.1,"charN":"B","stringN":"Str1","enumN":"NEUTRAL","intDataN":null,"listInt":[1,2,3],"listIntN":[4,5,null],"listNInt":[6,7,8],"listNIntN":[null,9,10],"listListEnumN":[["NEGATIVE",null]],"listIntData":[{"intV":1},{"intV":2},{"intV":3}],"listIntDataN":[{"intV":1},null,{"intV":3}],"tree":{"name":"root","left":{"name":"left","left":null,"right":null},"right":{"name":"right","left":{"name":"right.left","left":null,"right":null},"right":{"name":"right.right","left":null,"right":null}}},"mapStringInt":{"one":1,"two":2,"three":3},"mapIntStringN":{"0":null,"1":"first","2":"second"},"arrays":{"arrByte":[1,2,3],"arrInt":[100,200,300],"arrIntN":[null,-1,-2],"arrIntData":[{"intV":1},{"intV":2}]}} + """.trimIndent() + + val goldenValue2 = """ + {"unit":{},"boolean":true,"byte":10,"short":20,"int":30,"long":40,"float":50.5,"double":60.5,"char":"A","string":"Str0","enum":"POSITIVE","intData":{"intV":70},"unitN":null,"booleanN":null,"byteN":11,"shortN":21,"intN":31,"longN":41,"floatN":51.5,"doubleN":61.5,"charN":"B","stringN":"Str1","enumN":"NEUTRAL","intDataN":null,"listInt":[1,2,3],"listIntN":[4,5,null],"listNInt":[6,7,8],"listNIntN":[null,9,10],"listListEnumN":[["NEGATIVE",null]],"listIntData":[{"intV":1},{"intV":2},{"intV":3}],"listIntDataN":[{"intV":1},null,{"intV":3}],"tree":{"name":"root","left":{"name":"left","left":null,"right":null},"right":{"name":"right","left":{"name":"right.left","left":null,"right":null},"right":{"name":"right.right","left":null,"right":null}}},"mapStringInt":{"one":1,"two":2,"three":3},"mapIntStringN":{"0":null,"1":"first","2":"second"},"arrays":{"arrByte":[1,2,3],"arrInt":[100,200,300],"arrIntN":[null,-1,-2],"arrIntData":[{"intV":1},{"intV":2}]}} + """.trimIndent() + + private fun testSerializationImpl(typesUmbrella: TypesUmbrella, goldenValue: String) = parametrizedTest { jsonTestingMode -> + val json = default.encodeToString(TypesUmbrella.serializer(), typesUmbrella) + assertEquals(goldenValue, json) + val instance = default.decodeFromString(TypesUmbrella.serializer(), json, jsonTestingMode) + assertEquals(typesUmbrella, instance) + assertNotSame(typesUmbrella, instance) + } + + @Test + fun testSerialization() { + if (isWasm()) return //https://youtrack.jetbrains.com/issue/KT-59118/WASM-floating-point-toString-inconsistencies + testSerializationImpl(umbrellaInstance, goldenValue) + } + + @Test + fun testSerialization2() = testSerializationImpl(umbrellaInstance2, goldenValue2) + + @Test + fun testTopLevelPrimitive() = parametrizedTest { jsonTestingMode -> + testPrimitive(Unit, "{}", jsonTestingMode) + testPrimitive(false, "false", jsonTestingMode) + testPrimitive(1.toByte(), "1", jsonTestingMode) + testPrimitive(2.toShort(), "2", jsonTestingMode) + testPrimitive(3, "3", jsonTestingMode) + testPrimitive(4L, "4", jsonTestingMode) + testPrimitive(2.5f, "2.5", jsonTestingMode) + testPrimitive(3.5, "3.5", jsonTestingMode) + testPrimitive('c', "\"c\"", jsonTestingMode) + testPrimitive("string", "\"string\"", jsonTestingMode) + } + + private inline fun <reified T : Any> testPrimitive(primitive: T, expectedJson: String, jsonTestingMode: JsonTestingMode) { + val json = default.encodeToString(primitive, jsonTestingMode) + assertEquals(expectedJson, json) + val instance = default.decodeFromString<T>(json, jsonTestingMode) + assertEquals(primitive, instance) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/DecodeFromJsonElementTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/DecodeFromJsonElementTest.kt new file mode 100644 index 00000000..5db3d773 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/DecodeFromJsonElementTest.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlin.test.* + +class DecodeFromJsonElementTest { + @Serializable + data class A(val a: Int) + + @Serializable + data class B(val a: A?) + + @Test + fun testDecodeTopLevelNullable() { + val a = A(42) + val jsonElement = Json.encodeToJsonElement(a) + assertEquals(a, Json.decodeFromJsonElement<A?>(jsonElement)) + } + + @Test + fun topLevelNull() { + assertNull(Json.decodeFromJsonElement<A?>(JsonNull)) + } + + @Test + fun testInnerNullable() { + val b = B(A(42)) + val json = Json.encodeToJsonElement(b) + assertEquals(b, Json.decodeFromJsonElement(json)) + } + + @Test + fun testInnerNullableNull() { + val b = B(null) + val json = Json.encodeToJsonElement(b) + assertEquals(b, Json.decodeFromJsonElement(json)) + } + + @Test + fun testPrimitive() { + assertEquals(42, Json.decodeFromJsonElement(JsonPrimitive(42))) + assertEquals(42, Json.decodeFromJsonElement<Int?>(JsonPrimitive(42))) + assertEquals(null, Json.decodeFromJsonElement<Int?>(JsonNull)) + } + + @Test + fun testNullableList() { + assertEquals(listOf(42), Json.decodeFromJsonElement<List<Int>?>(JsonArray(listOf(JsonPrimitive(42))))) + assertEquals(listOf(42), Json.decodeFromJsonElement<List<Int?>?>(JsonArray(listOf(JsonPrimitive(42))))) + assertEquals(listOf(42), Json.decodeFromJsonElement<List<Int?>>(JsonArray(listOf(JsonPrimitive(42))))) + // Nulls + assertEquals(null, Json.decodeFromJsonElement<List<Int>?>(JsonNull)) + assertEquals(null, Json.decodeFromJsonElement<List<Int?>?>(JsonNull)) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonBuildersTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonBuildersTest.kt new file mode 100644 index 00000000..c2dab089 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonBuildersTest.kt @@ -0,0 +1,147 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlin.test.* + +class JsonBuildersTest { + + @Test + fun testBuildJson() { + val json = buildJsonObject { + putJsonObject("object") { + put("k", JsonPrimitive("v")) + } + + putJsonArray("array") { + addJsonObject { put("nestedLiteral", true) } + } + + val number: Number? = null + put("null", number) + put("primitive", JsonPrimitive(42)) + put("boolean", true) + put("literal", "foo") + put("null2", null) + } + assertEquals("""{"object":{"k":"v"},"array":[{"nestedLiteral":true}],"null":null,"primitive":42,"boolean":true,"literal":"foo","null2":null}""", json.toString()) + } + + @Test + fun testBuildJsonArray() { + val json = buildJsonArray { + add(true) + addJsonArray { + for (i in 1..10) add(i) + } + add(null) + addJsonObject { + put("stringKey", "stringValue") + } + } + assertEquals("""[true,[1,2,3,4,5,6,7,8,9,10],null,{"stringKey":"stringValue"}]""", json.toString()) + } + + @Test + fun testBuildJsonArrayAddAll() { + assertEquals( + """[1,2,3,4,5,null]""", + buildJsonArray { + assertTrue { addAll(listOf(1, 2, 3, 4, 5, null)) } + }.toString() + ) + + assertEquals( + """["a","b","c",null]""", + buildJsonArray { + assertTrue { addAll(listOf("a", "b", "c", null)) } + }.toString() + ) + + assertEquals( + """[true,false,null]""", + buildJsonArray { + assertTrue { addAll(listOf(true, false, null)) } + }.toString() + ) + + assertEquals( + """[2,"b",true,null]""", + buildJsonArray { + assertTrue { + addAll( + listOf( + JsonPrimitive(2), + JsonPrimitive("b"), + JsonPrimitive(true), + JsonNull, + ) + ) + } + }.toString() + ) + + assertEquals( + """[{},{},{},null]""", + buildJsonArray { + assertTrue { + addAll( + listOf( + JsonObject(emptyMap()), + JsonObject(emptyMap()), + JsonObject(emptyMap()), + JsonNull + ) + ) + } + }.toString() + ) + + assertEquals( + """[[],[],[],null]""", + buildJsonArray { + assertTrue { + addAll( + listOf( + JsonArray(emptyList()), + JsonArray(emptyList()), + JsonArray(emptyList()), + JsonNull + ) + ) + } + }.toString() + ) + + assertEquals( + """[null,null]""", + buildJsonArray { + assertTrue { + addAll(listOf(JsonNull, JsonNull)) + } + }.toString() + ) + } + + @Test + fun testBuildJsonArrayAddAllNotModified() { + assertEquals( + """[]""", + buildJsonArray { + // add collections + assertFalse { addAll(listOf<Number>()) } + assertFalse { addAll(listOf<String>()) } + assertFalse { addAll(listOf<Boolean>()) } + + // add json elements + assertFalse { addAll(listOf()) } + assertFalse { addAll(listOf<JsonNull>()) } + assertFalse { addAll(listOf<JsonObject>()) } + assertFalse { addAll(listOf<JsonArray>()) } + assertFalse { addAll(listOf<JsonPrimitive>()) } + }.toString() + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonChunkedStringDecoderTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonChunkedStringDecoderTest.kt new file mode 100644 index 00000000..fca258fb --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonChunkedStringDecoderTest.kt @@ -0,0 +1,74 @@ +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.test.assertFailsWithMessage +import kotlin.test.* + + +@Serializable(with = LargeStringSerializer::class) +data class LargeStringData(val largeString: String) + +@Serializable +data class ClassWithLargeStringDataField(val largeStringField: LargeStringData) + + +object LargeStringSerializer : KSerializer<LargeStringData> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LargeStringContent", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): LargeStringData { + require(decoder is ChunkedDecoder) { "Only chunked decoder supported" } + + val outStringBuilder = StringBuilder() + + decoder.decodeStringChunked { chunk -> + outStringBuilder.append(chunk) + } + return LargeStringData(outStringBuilder.toString()) + } + + override fun serialize(encoder: Encoder, value: LargeStringData) { + encoder.encodeString(value.largeString) + } +} + +open class JsonChunkedStringDecoderTest : JsonTestBase() { + + @Test + fun decodePlainLenientString() { + val longString = "abcd".repeat(8192) // Make string more than 16k + val sourceObject = ClassWithLargeStringDataField(LargeStringData(longString)) + val serializedObject = "{\"largeStringField\": $longString }" + val jsonWithLenientMode = Json { isLenient = true } + testDecodeInAllModes(jsonWithLenientMode, serializedObject, sourceObject) + } + + @Test + fun decodePlainString() { + val longStringWithEscape = "${"abcd".repeat(4096)}\"${"abcd".repeat(4096)}" // Make string more than 16k + val sourceObject = ClassWithLargeStringDataField(LargeStringData(longStringWithEscape)) + val serializedObject = Json.encodeToString(sourceObject) + testDecodeInAllModes(Json, serializedObject, sourceObject) + } + + private fun testDecodeInAllModes( + seralizer: Json, serializedObject: String, sourceObject: ClassWithLargeStringDataField + ) { + /* Filter out Java Streams mode in common tests. Java streams tested separately in java tests */ + JsonTestingMode.values().filterNot { it == JsonTestingMode.JAVA_STREAMS }.forEach { mode -> + if (mode == JsonTestingMode.TREE) { + assertFailsWithMessage<IllegalArgumentException>( + "Only chunked decoder supported", "Shouldn't decode JSON in TREE mode" + ) { + seralizer.decodeFromString<ClassWithLargeStringDataField>(serializedObject, mode) + } + } else { + val deserializedObject = + seralizer.decodeFromString<ClassWithLargeStringDataField>(serializedObject, mode) + assertEquals(sourceObject.largeStringField, deserializedObject.largeStringField) + } + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonCoerceInputValuesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonCoerceInputValuesTest.kt new file mode 100644 index 00000000..3d7c3322 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonCoerceInputValuesTest.kt @@ -0,0 +1,145 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.test.assertFailsWithSerial +import kotlin.test.* + +class JsonCoerceInputValuesTest : JsonTestBase() { + @Serializable + data class WithBoolean(val b: Boolean = false) + + @Serializable + data class WithEnum(val e: SampleEnum = SampleEnum.OptionC) + + @Serializable + data class MultipleValues( + val data: StringData, + val data2: IntData = IntData(0), + val i: Int = 42, + val e: SampleEnum = SampleEnum.OptionA, + val foo: String + ) + + @Serializable + data class NullableEnumHolder( + val enum: SampleEnum? + ) + + @Serializable + class Uncoercable( + val s: String + ) + + @Serializable + class UncoercableEnum( + val e: SampleEnum + ) + + val json = Json { + coerceInputValues = true + isLenient = true + } + + private fun <T> doTest(inputs: List<String>, expected: T, serializer: KSerializer<T>) { + for (input in inputs) { + parametrizedTest(json) { + assertEquals(expected, decodeFromString(serializer, input), "Failed on input: $input") + } + } + } + + @Test + fun testUseDefaultOnNonNullableBoolean() = doTest( + listOf( + """{"b":false}""", + """{"b":null}""", + """{}""", + ), + WithBoolean(), + WithBoolean.serializer() + ) + + @Test + fun testUseDefaultOnUnknownEnum() { + doTest( + listOf( + """{"e":unknown_value}""", + """{"e":"unknown_value"}""", + """{"e":null}""", + """{}""", + ), + WithEnum(), + WithEnum.serializer() + ) + assertFailsWithSerial("JsonDecodingException") { + json.decodeFromString(WithEnum.serializer(), """{"e":{"x":"definitely not a valid enum value"}}""") + } + assertFailsWithSerial("JsonDecodingException") { // test user still sees exception on missing quotes + Json(json) { isLenient = false }.decodeFromString(WithEnum.serializer(), """{"e":unknown_value}""") + } + } + + @Test + fun testUseDefaultInMultipleCases() { + val testData = mapOf( + """{"data":{"data":"foo"},"data2":null,"i":null,"e":null,"foo":"bar"}""" to MultipleValues( + StringData("foo"), + foo = "bar" + ), + """{"data":{"data":"foo"},"data2":{"intV":42},"i":null,"e":null,"foo":"bar"}""" to MultipleValues( + StringData( + "foo" + ), IntData(42), foo = "bar" + ), + """{"data":{"data":"foo"},"data2":{"intV":42},"i":0,"e":"NoOption","foo":"bar"}""" to MultipleValues( + StringData("foo"), + IntData(42), + i = 0, + foo = "bar" + ), + """{"data":{"data":"foo"},"data2":{"intV":42},"i":0,"e":"OptionC","foo":"bar"}""" to MultipleValues( + StringData("foo"), + IntData(42), + i = 0, + e = SampleEnum.OptionC, + foo = "bar" + ), + ) + for ((input, expected) in testData) { + assertEquals(expected, json.decodeFromString(MultipleValues.serializer(), input), "Failed on input: $input") + } + } + + @Test + fun testNullSupportForEnums() = parametrizedTest(json) { + var decoded = decodeFromString<NullableEnumHolder>("""{"enum": null}""") + assertNull(decoded.enum) + + decoded = decodeFromString<NullableEnumHolder>("""{"enum": OptionA}""") + assertEquals(SampleEnum.OptionA, decoded.enum) + } + + @Test + fun propertiesWithoutDefaultValuesDoNotChangeErrorMsg() { + val json2 = Json(json) { coerceInputValues = false } + parametrizedTest { mode -> + val e1 = assertFailsWith<SerializationException>() { json.decodeFromString<Uncoercable>("""{"s":null}""", mode) } + val e2 = assertFailsWith<SerializationException>() { json2.decodeFromString<Uncoercable>("""{"s":null}""", mode) } + assertEquals(e2.message, e1.message) + } + } + + @Test + fun propertiesWithoutDefaultValuesDoNotChangeErrorMsgEnum() { + val json2 = Json(json) { coerceInputValues = false } + parametrizedTest { mode -> + val e1 = assertFailsWith<SerializationException> { json.decodeFromString<UncoercableEnum>("""{"e":"UNEXPECTED"}""", mode) } + val e2 = assertFailsWith<SerializationException> { json2.decodeFromString<UncoercableEnum>("""{"e":"UNEXPECTED"}""", mode) } + assertEquals(e2.message, e1.message) + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonConfigurationTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonConfigurationTest.kt new file mode 100644 index 00000000..2a4dc2c1 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonConfigurationTest.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlin.test.* + +class JsonConfigurationTest { + + @Test + fun testPrettyPrint() { + json(true, "") + json(true, "\n") + json(true, "\r") + json(true, "\t") + json(true, " ") + json(true, " ") + json(true, " \t\r\n\t ") + assertFailsWith<IllegalArgumentException> { json(false, " ") } + assertFailsWith<IllegalArgumentException> { json(false, " ") } + assertFailsWith<IllegalArgumentException> { json(true, "f") } + assertFailsWith<IllegalArgumentException> { json(true, "\tf\n") } + } + + private fun json(prettyPrint: Boolean, prettyPrintIndent: String) = Json { + this.prettyPrint = prettyPrint + this.prettyPrintIndent = prettyPrintIndent + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonCustomSerializersTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonCustomSerializersTest.kt new file mode 100644 index 00000000..351866df --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonCustomSerializersTest.kt @@ -0,0 +1,354 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +@file:UseContextualSerialization(JsonCustomSerializersTest.B::class) + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.modules.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class JsonCustomSerializersTest : JsonTestBase() { + + @Serializable + data class A(@Id(1) val b: B) + + data class B(@Id(1) val value: Int) + + object BSerializer : KSerializer<B> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("B", PrimitiveKind.INT) + override fun serialize(encoder: Encoder, value: B) { + encoder.encodeInt(value.value) + } + + override fun deserialize(decoder: Decoder): B { + return B(decoder.decodeInt()) + } + } + + @Serializable + data class BList(@Id(1) val bs: List<B>) + + @Serializable(C.Companion::class) + data class C(@Id(1) val a: Int = 31, @Id(2) val b: Int = 42) { + @Serializer(forClass = C::class) + companion object : KSerializer<C> { + override fun serialize(encoder: Encoder, value: C) { + val elemOutput = encoder.beginStructure(descriptor) + elemOutput.encodeIntElement(descriptor, 1, value.b) + if (value.a != 31) elemOutput.encodeIntElement(descriptor, 0, value.a) + elemOutput.endStructure(descriptor) + } + } + } + + @Serializable + data class CList1(@Id(1) val c: List<C>) + + @Serializable(CList2.Companion::class) + data class CList2(@Id(1) val d: Int = 5, @Id(2) val c: List<C>) { + @Serializer(forClass = CList2::class) + companion object : KSerializer<CList2> { + override fun serialize(encoder: Encoder, value: CList2) { + val elemOutput = encoder.beginStructure(descriptor) + elemOutput.encodeSerializableElement(descriptor, 1, ListSerializer(C), value.c) + if (value.d != 5) elemOutput.encodeIntElement(descriptor, 0, value.d) + elemOutput.endStructure(descriptor) + } + } + } + + @Serializable(CList3.Companion::class) + data class CList3(@Id(1) val e: List<C> = emptyList(), @Id(2) val f: Int) { + @Serializer(forClass = CList3::class) + companion object : KSerializer<CList3> { + override fun serialize(encoder: Encoder, value: CList3) { + val elemOutput = encoder.beginStructure(descriptor) + if (value.e.isNotEmpty()) elemOutput.encodeSerializableElement(descriptor, 0, ListSerializer(C), value.e) + elemOutput.encodeIntElement(descriptor, 1, value.f) + elemOutput.endStructure(descriptor) + } + } + } + + @Serializable(CList4.Companion::class) + data class CList4(@Id(1) val g: List<C> = emptyList(), @Id(2) val h: Int) { + @Serializer(forClass = CList4::class) + companion object : KSerializer<CList4> { + override fun serialize(encoder: Encoder, value: CList4) { + val elemOutput = encoder.beginStructure(descriptor) + elemOutput.encodeIntElement(descriptor, 1, value.h) + if (value.g.isNotEmpty()) elemOutput.encodeSerializableElement(descriptor, 0, ListSerializer(C), value.g) + elemOutput.endStructure(descriptor) + } + } + } + + @Serializable(CList5.Companion::class) + data class CList5(@Id(1) val g: List<Int> = emptyList(), @Id(2) val h: Int) { + @Serializer(forClass = CList5::class) + companion object : KSerializer<CList5> { + override fun serialize(encoder: Encoder, value: CList5) { + val elemOutput = encoder.beginStructure(descriptor) + elemOutput.encodeIntElement(descriptor, 1, value.h) + if (value.g.isNotEmpty()) elemOutput.encodeSerializableElement( + descriptor, 0, ListSerializer(Int.serializer()), + value.g + ) + elemOutput.endStructure(descriptor) + } + } + } + + private val moduleWithB = serializersModuleOf(B::class, BSerializer) + + private fun createJsonWithB() = Json { isLenient = true; serializersModule = moduleWithB; useAlternativeNames = false } + // useAlternativeNames uses SerialDescriptor.hashCode, + // which is unavailable for partially-customized serializers such as in this file + private val jsonNoAltNames = Json { useAlternativeNames = false } + + @Test + fun testWriteCustom() = parametrizedTest { jsonTestingMode -> + val a = A(B(2)) + val j = createJsonWithB() + val s = j.encodeToString(a, jsonTestingMode) + assertEquals("""{"b":2}""", s) + } + + @Test + fun testReadCustom() = parametrizedTest { jsonTestingMode -> + val a = A(B(2)) + val j = createJsonWithB() + val s = j.decodeFromString<A>("{b:2}", jsonTestingMode) + assertEquals(a, s) + } + + @Test + fun testWriteCustomList() = parametrizedTest { jsonTestingMode -> + val obj = BList(listOf(B(1), B(2), B(3))) + val j = createJsonWithB() + val s = j.encodeToString(obj, jsonTestingMode) + assertEquals("""{"bs":[1,2,3]}""", s) + } + + @Test + fun testReadCustomList() = parametrizedTest { jsonTestingMode -> + val obj = BList(listOf(B(1), B(2), B(3))) + val j = createJsonWithB() + val bs = j.decodeFromString<BList>("{bs:[1,2,3]}", jsonTestingMode) + assertEquals(obj, bs) + } + + @Test + fun testWriteCustomListRootLevel() = parametrizedTest { jsonTestingMode -> + val obj = listOf(B(1), B(2), B(3)) + val j = createJsonWithB() + val s = j.encodeToString(ListSerializer(BSerializer), obj, jsonTestingMode) + assertEquals("[1,2,3]", s) + } + + @Test + fun testReadCustomListRootLevel() = parametrizedTest { jsonTestingMode -> + val obj = listOf(B(1), B(2), B(3)) + val j = createJsonWithB() + val bs = j.decodeFromString(ListSerializer(BSerializer), "[1,2,3]", jsonTestingMode) + assertEquals(obj, bs) + } + + @Test + fun testWriteCustomInvertedOrder() = parametrizedTest { jsonTestingMode -> + val obj = C(1, 2) + val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode) + assertEquals("""{"b":2,"a":1}""", s) + } + + @Test + fun testWriteCustomOmitDefault() = parametrizedTest { jsonTestingMode -> + val obj = C(b = 2) + val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode) + assertEquals("""{"b":2}""", s) + } + + @Test + fun testReadCustomInvertedOrder() = parametrizedTest { jsonTestingMode -> + val obj = C(1, 2) + val s = jsonNoAltNames.decodeFromString<C>("""{"b":2,"a":1}""", jsonTestingMode) + assertEquals(obj, s) + } + + @Test + fun testReadCustomOmitDefault() = parametrizedTest { jsonTestingMode -> + val obj = C(b = 2) + val s = jsonNoAltNames.decodeFromString<C>("""{"b":2}""", jsonTestingMode) + assertEquals(obj, s) + } + + @Test + fun testWriteListOfOptional() = parametrizedTest { jsonTestingMode -> + val obj = listOf(C(a = 1), C(b = 2), C(3, 4)) + val s = jsonNoAltNames.encodeToString(ListSerializer(C), obj, jsonTestingMode) + assertEquals("""[{"b":42,"a":1},{"b":2},{"b":4,"a":3}]""", s) + } + + @Test + fun testReadListOfOptional() = parametrizedTest { jsonTestingMode -> + val obj = listOf(C(a = 1), C(b = 2), C(3, 4)) + val j = """[{"b":42,"a":1},{"b":2},{"b":4,"a":3}]""" + val s = jsonNoAltNames.decodeFromString(ListSerializer<C>(C), j, jsonTestingMode) + assertEquals(obj, s) + } + + @Test + fun testWriteOptionalList1() = parametrizedTest { jsonTestingMode -> + val obj = CList1(listOf(C(a = 1), C(b = 2), C(3, 4))) + val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode) + assertEquals("""{"c":[{"b":42,"a":1},{"b":2},{"b":4,"a":3}]}""", s) + } + + @Test + fun testWriteOptionalList1Quoted() = parametrizedTest { jsonTestingMode -> + val obj = CList1(listOf(C(a = 1), C(b = 2), C(3, 4))) + val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode) + assertEquals("""{"c":[{"b":42,"a":1},{"b":2},{"b":4,"a":3}]}""", s) + } + + @Test + fun testReadOptionalList1() = parametrizedTest { jsonTestingMode -> + val obj = CList1(listOf(C(a = 1), C(b = 2), C(3, 4))) + val j = """{"c":[{"b":42,"a":1},{"b":2},{"b":4,"a":3}]}""" + assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode)) + } + + @Test + fun testWriteOptionalList2a() = parametrizedTest { jsonTestingMode -> + val obj = CList2(7, listOf(C(a = 5), C(b = 6), C(7, 8))) + val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode) + assertEquals("""{"c":[{"b":42,"a":5},{"b":6},{"b":8,"a":7}],"d":7}""", s) + } + + @Test + fun testReadOptionalList2a() = parametrizedTest { jsonTestingMode -> + val obj = CList2(7, listOf(C(a = 5), C(b = 6), C(7, 8))) + val j = """{"c":[{"b":42,"a":5},{"b":6},{"b":8,"a":7}],"d":7}""" + assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode)) + } + + @Test + fun testWriteOptionalList2b() = parametrizedTest { jsonTestingMode -> + val obj = CList2(c = listOf(C(a = 5), C(b = 6), C(7, 8))) + val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode) + assertEquals("""{"c":[{"b":42,"a":5},{"b":6},{"b":8,"a":7}]}""", s) + } + + @Test + fun testReadOptionalList2b() = parametrizedTest { jsonTestingMode -> + val obj = CList2(c = listOf(C(a = 5), C(b = 6), C(7, 8))) + val j = """{"c":[{"b":42,"a":5},{"b":6},{"b":8,"a":7}]}""" + assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode)) + } + + @Test + fun testWriteOptionalList3a() = parametrizedTest { jsonTestingMode -> + val obj = CList3(listOf(C(a = 1), C(b = 2), C(3, 4)), 99) + val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode) + assertEquals("""{"e":[{"b":42,"a":1},{"b":2},{"b":4,"a":3}],"f":99}""", s) + } + + @Test + fun testReadOptionalList3a() = parametrizedTest { jsonTestingMode -> + val obj = CList3(listOf(C(a = 1), C(b = 2), C(3, 4)), 99) + val j = """{"e":[{"b":42,"a":1},{"b":2},{"b":4,"a":3}],"f":99}""" + assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode)) + } + + @Test + fun testWriteOptionalList3b() = parametrizedTest { jsonTestingMode -> + val obj = CList3(f = 99) + val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode) + assertEquals("""{"f":99}""", s) + } + + @Test + fun testReadOptionalList3b() = parametrizedTest { jsonTestingMode -> + val obj = CList3(f = 99) + val j = """{"f":99}""" + assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode)) + } + + @Test + fun testWriteOptionalList4a() = parametrizedTest { jsonTestingMode -> + val obj = CList4(listOf(C(a = 1), C(b = 2), C(3, 4)), 54) + val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode) + assertEquals("""{"h":54,"g":[{"b":42,"a":1},{"b":2},{"b":4,"a":3}]}""", s) + } + + @Test + fun testReadOptionalList4a() = parametrizedTest { jsonTestingMode -> + val obj = CList4(listOf(C(a = 1), C(b = 2), C(3, 4)), 54) + val j = """{"h":54,"g":[{"b":42,"a":1},{"b":2},{"b":4,"a":3}]}""" + assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode)) + } + + @Test + fun testWriteOptionalList4b() = parametrizedTest { jsonTestingMode -> + val obj = CList4(h = 97) + val j = """{"h":97}""" + val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode) + assertEquals(j, s) + } + + @Test + fun testReadOptionalList4b() = parametrizedTest { jsonTestingMode -> + val obj = CList4(h = 97) + val j = """{"h":97}""" + assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode)) + } + + @Test + fun testWriteOptionalList5a() = parametrizedTest { jsonTestingMode -> + val obj = CList5(listOf(9, 8, 7, 6, 5), 5) + val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode) + assertEquals("""{"h":5,"g":[9,8,7,6,5]}""", s) + } + + @Test + fun testReadOptionalList5a() = parametrizedTest { jsonTestingMode -> + val obj = CList5(listOf(9, 8, 7, 6, 5), 5) + val j = """{"h":5,"g":[9,8,7,6,5]}""" + assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode)) + } + + @Test + fun testWriteOptionalList5b() = parametrizedTest { jsonTestingMode -> + val obj = CList5(h = 999) + val s = jsonNoAltNames.encodeToString(obj, jsonTestingMode) + assertEquals("""{"h":999}""", s) + } + + @Test + fun testReadOptionalList5b() = parametrizedTest { jsonTestingMode -> + val obj = CList5(h = 999) + val j = """{"h":999}""" + assertEquals(obj, jsonNoAltNames.decodeFromString(j, jsonTestingMode)) + } + + @Test + fun testMapBuiltinsTest() = parametrizedTest { jsonTestingMode -> + val map = mapOf(1 to "1", 2 to "2") + val serial = MapSerializer(Int.serializer(), String.serializer()) + val s = jsonNoAltNames.encodeToString(serial, map, jsonTestingMode) + assertEquals("""{"1":"1","2":"2"}""", s) + } + + @Test + fun testResolveAtRootLevel() = parametrizedTest { jsonTestingMode -> + val j = createJsonWithB() + val bs = j.decodeFromString<B>("1", jsonTestingMode) + assertEquals(B(1), bs) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonDefaultContextTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonDefaultContextTest.kt new file mode 100644 index 00000000..af54c31b --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonDefaultContextTest.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlin.test.* + +class JsonDefaultContextTest { + + @Test + fun testRepeatedSerializer() { + // #616 + val json = Json + Json { serializersModule = json.serializersModule } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonElementDecodingTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonElementDecodingTest.kt new file mode 100644 index 00000000..3cdfa082 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonElementDecodingTest.kt @@ -0,0 +1,110 @@ +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlin.test.* + +class JsonElementDecodingTest : JsonTestBase() { + + @Serializable + data class A(val a: Int = 42) + + @Test + fun testTopLevelClass() = assertSerializedForm(A(), """{}""".trimMargin()) + + @Test + fun testTopLevelNullableClass() { + assertSerializedForm<A?>(A(), """{}""") + assertSerializedForm<A?>(null, "null") + } + + @Test + fun testTopLevelPrimitive() = assertSerializedForm(42, """42""") + + @Test + fun testTopLevelNullablePrimitive() { + assertSerializedForm<Int?>(42, """42""") + assertSerializedForm<Int?>(null, """null""") + } + + @Test + fun testTopLevelList() = assertSerializedForm(listOf(42), """[42]""") + + @Test + fun testTopLevelNullableList() { + assertSerializedForm<List<Int>?>(listOf(42), """[42]""") + assertSerializedForm<List<Int>?>(null, """null""") + } + + private inline fun <reified T> assertSerializedForm(value: T, expectedString: String) { + val element = Json.encodeToJsonElement(value) + assertEquals(expectedString, element.toString()) + assertEquals(value, Json.decodeFromJsonElement(element)) + } + + @Test + fun testDeepRecursion() { + // Reported as https://github.com/Kotlin/kotlinx.serialization/issues/1594 + var json = """{ "a": %}""" + for (i in 0..12) { + json = json.replace("%", json) + } + json = json.replace("%", "0") + Json.parseToJsonElement(json) + } + + private open class NullAsElementSerializer<T : Any>(private val serializer: KSerializer<T>, val nullElement: T) : KSerializer<T?> { + final override val descriptor: SerialDescriptor = serializer.descriptor.nullable + + final override fun serialize(encoder: Encoder, value: T?) { + serializer.serialize(encoder, value ?: nullElement) + } + + final override fun deserialize(decoder: Decoder): T = serializer.deserialize(decoder) + } + + private object NullAsJsonNullJsonElementSerializer : NullAsElementSerializer<JsonElement>(JsonElement.serializer(), JsonNull) + private object NullAsJsonNullJsonPrimitiveSerializer : NullAsElementSerializer<JsonPrimitive>(JsonPrimitive.serializer(), JsonNull) + private object NullAsJsonNullJsonNullSerializer : NullAsElementSerializer<JsonNull>(JsonNull.serializer(), JsonNull) + private val noExplicitNullsOrDefaultsJson = Json { + explicitNulls = false + encodeDefaults = false + } + + @Test + fun testNullableJsonElementDecoding() { + @Serializable + data class Wrapper( + @Serializable(NullAsJsonNullJsonElementSerializer::class) + val value: JsonElement? = null, + ) + + assertJsonFormAndRestored(Wrapper.serializer(), Wrapper(value = JsonNull), """{"value":null}""", noExplicitNullsOrDefaultsJson) + assertJsonFormAndRestored(Wrapper.serializer(), Wrapper(value = null), """{}""", noExplicitNullsOrDefaultsJson) + } + + @Test + fun testNullableJsonPrimitiveDecoding() { + @Serializable + data class Wrapper( + @Serializable(NullAsJsonNullJsonPrimitiveSerializer::class) + val value: JsonPrimitive? = null, + ) + + assertJsonFormAndRestored(Wrapper.serializer(), Wrapper(value = JsonNull), """{"value":null}""", noExplicitNullsOrDefaultsJson) + assertJsonFormAndRestored(Wrapper.serializer(), Wrapper(value = null), """{}""", noExplicitNullsOrDefaultsJson) + } + + @Test + fun testNullableJsonNullDecoding() { + @Serializable + data class Wrapper( + @Serializable(NullAsJsonNullJsonNullSerializer::class) + val value: JsonNull? = null, + ) + + assertJsonFormAndRestored(Wrapper.serializer(), Wrapper(value = JsonNull), """{"value":null}""", noExplicitNullsOrDefaultsJson) + assertJsonFormAndRestored(Wrapper.serializer(), Wrapper(value = null), """{}""", noExplicitNullsOrDefaultsJson) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonEncoderDecoderRecursiveTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonEncoderDecoderRecursiveTest.kt new file mode 100644 index 00000000..b4f7c716 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonEncoderDecoderRecursiveTest.kt @@ -0,0 +1,211 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlin.test.* + +class JsonEncoderDecoderRecursiveTest : JsonTestBase() { + private val inputDataString = """{"id":0,"payload":{"from":42,"to":43,"msg":"Hello world"},"timestamp":1000}""" + private val inputErrorString = """{"id":1,"payload":{"error":"Connection timed out"},"timestamp":1001}""" + private val inputDataJson = default.parseToJsonElement(inputDataString) + private val inputErrorJson = default.parseToJsonElement(inputErrorString) + private val inputRecursive = + """{"type":"b","children":[{"type":"a","value":1},{"type":"a","value":2},{"type":"b","children":[]}]}""" + private val outputRecursive = SealedRecursive.B( + listOf(SealedRecursive.A(1), SealedRecursive.A(2), SealedRecursive.B(emptyList())) + ) + + @Test + fun testParseDataString() = parametrizedTest { streaming -> + val ev = default.decodeFromString(Event.serializer(), inputDataString, streaming) + with(ev) { + assertEquals(0, id) + assertEquals(Either.Right(Payload(42, 43, "Hello world")), payload) + assertEquals(1000, timestamp) + } + } + + @Test + fun testParseErrorString() = parametrizedTest { jsonTestingMode -> + val ev = default.decodeFromString(Event.serializer(), inputErrorString, jsonTestingMode) + with(ev) { + assertEquals(1, id) + assertEquals(Either.Left("Connection timed out"), payload) + assertEquals(1001, timestamp) + } + } + + @Test + fun testWriteDataString() = parametrizedTest { jsonTestingMode -> + val outputData = Event(0, Either.Right(Payload(42, 43, "Hello world")), 1000) + val ev = default.encodeToString(Event.serializer(), outputData, jsonTestingMode) + assertEquals(inputDataString, ev) + } + + @Test + fun testWriteDataStringIndented() = parametrizedTest { jsonTestingMode -> + val outputData = Event(0, Either.Right(Payload(42, 43, "Hello world")), 1000) + val ev = Json { prettyPrint = true }.encodeToString(Event.serializer(), outputData, jsonTestingMode) + assertEquals("""{ + | "id": 0, + | "payload": { + | "from": 42, + | "to": 43, + | "msg": "Hello world" + | }, + | "timestamp": 1000 + |}""".trimMargin(), ev) + } + + @Test + fun testWriteErrorString() = parametrizedTest { jsonTestingMode -> + val outputError = Event(1, Either.Left("Connection timed out"), 1001) + val ev = default.encodeToString(Event.serializer(), outputError, jsonTestingMode) + assertEquals(inputErrorString, ev) + } + + @Test + fun testParseDataJson() { + val ev = default.decodeFromJsonElement(Event.serializer(), inputDataJson) + with(ev) { + assertEquals(0, id) + assertEquals(Either.Right(Payload(42, 43, "Hello world")), payload) + assertEquals(1000, timestamp) + } + } + + @Test + fun testParseErrorJson() { + val ev = default.decodeFromJsonElement(Event.serializer(), inputErrorJson) + with(ev) { + assertEquals(1, id) + assertEquals(Either.Left("Connection timed out"), payload) + assertEquals(1001, timestamp) + } + } + + @Test + fun testWriteDataJson() { + val outputData = Event(0, Either.Right(Payload(42, 43, "Hello world")), 1000) + val ev = default.encodeToJsonElement(Event.serializer(), outputData) + assertEquals(inputDataJson, ev) + } + + @Test + fun testWriteErrorJson() { + val outputError = Event(1, Either.Left("Connection timed out"), 1001) + val ev = default.encodeToJsonElement(Event.serializer(), outputError) + assertEquals(inputErrorJson, ev) + } + + @Test + fun testParseRecursive() = parametrizedTest { jsonTestingMode -> + val ev = default.decodeFromString(RecursiveSerializer, inputRecursive, jsonTestingMode) + assertEquals(outputRecursive, ev) + } + + @Test + fun testWriteRecursive() = parametrizedTest { jsonTestingMode -> + val ev = default.encodeToString(RecursiveSerializer, outputRecursive, jsonTestingMode) + assertEquals(inputRecursive, ev) + } + + @Serializable + private data class Payload(val from: Long, val to: Long, val msg: String) + + private sealed class Either { + data class Left(val errorMsg: String): Either() + data class Right(val data: Payload): Either() + } + + private object EitherSerializer: KSerializer<Either> { + override val descriptor: SerialDescriptor = buildSerialDescriptor("Either", PolymorphicKind.SEALED) { + val leftDescriptor = buildClassSerialDescriptor("Either.Left") { + element<String>("errorMsg") + } + val rightDescriptor = buildClassSerialDescriptor("Either.Right") { + element("data", Payload.serializer().descriptor) + } + element("left", leftDescriptor) + element("right", rightDescriptor) + } + + override fun deserialize(decoder: Decoder): Either { + val jsonReader = decoder as? JsonDecoder + ?: throw SerializationException("This class can be loaded only by JSON") + val tree = jsonReader.decodeJsonElement() as? JsonObject + ?: throw SerializationException("Expected JSON object") + if ("error" in tree) return Either.Left(tree.getValue("error").jsonPrimitive.content) + return Either.Right(decoder.json.decodeFromJsonElement(Payload.serializer(), tree)) + } + + override fun serialize(encoder: Encoder, value: Either) { + val jsonWriter = encoder as? JsonEncoder + ?: throw SerializationException("This class can be saved only by JSON") + val tree = when (value) { + is Either.Left -> JsonObject(mapOf("error" to JsonPrimitive(value.errorMsg))) + is Either.Right -> encoder.json.encodeToJsonElement(Payload.serializer(), value.data) + } + jsonWriter.encodeJsonElement(tree) + } + } + + @Serializable + private data class Event( + val id: Int, + @Serializable(with = EitherSerializer::class) val payload: Either, + val timestamp: Long + ) + + @Serializable(with = RecursiveSerializer::class) + private sealed class SealedRecursive { + @Serializable + data class A(val value: Int) : SealedRecursive() + + @Serializable + data class B(val children: List<SealedRecursive>) : SealedRecursive() + } + + private object RecursiveSerializer : KSerializer<SealedRecursive> { + private const val typeAttribute = "type" + private const val typeNameA = "a" + private const val typeNameB = "b" + + // TODO in builder is not suitable for recursive descriptors + override val descriptor: SerialDescriptor = buildSerialDescriptor("SealedRecursive", PolymorphicKind.SEALED) { + element("a", SealedRecursive.A.serializer().descriptor) + element("b", SealedRecursive.B.serializer().descriptor) + } + + override fun serialize(encoder: Encoder, value: SealedRecursive) { + if (encoder !is JsonEncoder) throw SerializationException("This class can be saved only by JSON") + val (tree, typeName) = when (value) { + is SealedRecursive.A -> encoder.json.encodeToJsonElement(SealedRecursive.A.serializer(), value) to typeNameA + is SealedRecursive.B -> encoder.json.encodeToJsonElement(SealedRecursive.B.serializer(), value) to typeNameB + } + val contents: MutableMap<String, JsonElement> = mutableMapOf(typeAttribute to JsonPrimitive(typeName)) + contents.putAll(tree.jsonObject) + val element = JsonObject(contents) + encoder.encodeJsonElement(element) + } + + override fun deserialize(decoder: Decoder): SealedRecursive { + val jsonReader = decoder as? JsonDecoder + ?: throw SerializationException("This class can be loaded only by JSON") + val tree = jsonReader.decodeJsonElement() as? JsonObject + ?: throw SerializationException("Expected JSON object") + val typeName = tree.getValue(typeAttribute).jsonPrimitive.content + val objTree = JsonObject(tree - typeAttribute) + return when (typeName) { + typeNameA -> decoder.json.decodeFromJsonElement(SealedRecursive.A.serializer(), objTree) + typeNameB -> decoder.json.decodeFromJsonElement(SealedRecursive.B.serializer(), objTree) + else -> throw SerializationException("Unknown type: $typeName") + } + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonErrorMessagesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonErrorMessagesTest.kt new file mode 100644 index 00000000..08d1eefd --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonErrorMessagesTest.kt @@ -0,0 +1,159 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.test.* +import kotlin.test.* + + +class JsonErrorMessagesTest : JsonTestBase() { + + @Test + fun testJsonTokensAreProperlyReported() = parametrizedTest { mode -> + val input1 = """{"boxed":4}""" + val input2 = """{"boxed":"str"}""" + + val serString = serializer<Box<String>>() + val serInt = serializer<Box<Int>>() + + checkSerializationException({ + default.decodeFromString(serString, input1, mode) + }, { message -> + if (mode == JsonTestingMode.TREE) + assertContains(message, "String literal for key 'boxed' should be quoted.") + else + assertContains( + message, + "Unexpected JSON token at offset 9: Expected quotation mark '\"', but had '4' instead at path: \$.boxed" + ) + }) + + checkSerializationException({ + default.decodeFromString(serInt, input2, mode) + }, { message -> + if (mode != JsonTestingMode.TREE) + // we allow number values to be quoted, so the message pointing to 's' is correct + assertContains( + message, + "Unexpected JSON token at offset 9: Unexpected symbol 's' in numeric literal at path: \$.boxed" + ) + else + assertContains(message, "Failed to parse literal as 'int' value") + }) + } + + @Test + fun testMissingClosingQuote() = parametrizedTest { mode -> + val input1 = """{"boxed:4}""" + val input2 = """{"boxed":"str}""" + val input3 = """{"boxed:"str"}""" + val serString = serializer<Box<String>>() + val serInt = serializer<Box<Int>>() + + checkSerializationException({ + default.decodeFromString(serInt, input1, mode) + }, { message -> + // For discussion: + // Technically, both of these messages are correct despite them being completely different. + // A `:` instead of `"` is a good guess, but `:`/`}` is a perfectly valid token inside Json string — for example, + // it can be some kind of path `{"foo:bar:baz":"my:resource:locator:{123}"}` or even URI used as a string key/value. + // So if the closing quote is missing, there's really no way to correctly tell where the key or value is supposed to end. + // Although we may try to unify these messages for consistency. + if (mode in setOf(JsonTestingMode.STREAMING, JsonTestingMode.TREE)) + assertContains( + message, + "Unexpected JSON token at offset 7: Expected quotation mark '\"', but had ':' instead at path: \$" + ) + else + assertContains( + message, "Unexpected EOF at path: \$" + ) + }) + + checkSerializationException({ + default.decodeFromString(serString, input2, mode) + }, { message -> + if (mode in setOf(JsonTestingMode.STREAMING, JsonTestingMode.TREE)) + assertContains( + message, + "Unexpected JSON token at offset 13: Expected quotation mark '\"', but had '}' instead at path: \$" + ) + else + assertContains(message, "Unexpected EOF at path: \$.boxed") + }) + + checkSerializationException({ + default.decodeFromString(serString, input3, mode) + }, { message -> + assertContains( + message, + "Unexpected JSON token at offset 9: Expected colon ':', but had 's' instead at path: \$" + ) + }) + } + + @Test + fun testUnquoted() = parametrizedTest { mode -> + val input1 = """{boxed:str}""" + val input2 = """{"boxed":str}""" + val ser = serializer<Box<String>>() + + checkSerializationException({ + default.decodeFromString(ser, input1, mode) + }, { message -> + assertContains( + message, + """Unexpected JSON token at offset 1: Expected quotation mark '"', but had 'b' instead at path: ${'$'}""" + ) + }) + + checkSerializationException({ + default.decodeFromString(ser, input2, mode) + }, { message -> + if (mode == JsonTestingMode.TREE) assertContains( + message, + """String literal for key 'boxed' should be quoted.""" + ) + else assertContains( + message, + """Unexpected JSON token at offset 9: Expected quotation mark '"', but had 's' instead at path: ${'$'}.boxed""" + ) + }) + } + + @Test + fun testNullLiteralForNotNull() = parametrizedTest { mode -> + val input = """{"boxed":null}""" + val ser = serializer<Box<String>>() + checkSerializationException({ + default.decodeFromString(ser, input, mode) + }, { message -> + if (mode == JsonTestingMode.TREE) + assertContains(message, "Unexpected 'null' literal when non-nullable string was expected") + else + assertContains( + message, + "Unexpected JSON token at offset 9: Expected string literal but 'null' literal was found at path: \$.boxed" + ) + }) + } + + @Test + fun testEof() = parametrizedTest { mode -> + val input = """{"boxed":""" + checkSerializationException({ + default.decodeFromString<Box<String>>(input, mode) + }, { message -> + if (mode == JsonTestingMode.TREE) + assertContains(message, "Cannot read Json element because of unexpected end of the input at path: $") + else + assertContains(message, "Expected quotation mark '\"', but had 'EOF' instead at path: \$.boxed") + + }) + + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonExponentTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonExponentTest.kt new file mode 100644 index 00000000..0f31ac50 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonExponentTest.kt @@ -0,0 +1,79 @@ +package kotlinx.serialization.json + +import kotlinx.serialization.Serializable +import kotlinx.serialization.test.* +import kotlin.test.Test +import kotlin.test.assertEquals + +class JsonExponentTest : JsonTestBase() { + @Serializable + data class SomeData(val count: Long) + @Serializable + data class SomeDataDouble(val count: Double) + + @Test + fun testExponentDecodingPositive() = parametrizedTest { + val decoded = Json.decodeFromString<SomeData>("""{ "count": 23e11 }""", it) + assertEquals(2300000000000, decoded.count) + } + + @Test + fun testExponentDecodingNegative() = parametrizedTest { + val decoded = Json.decodeFromString<SomeData>("""{ "count": -10E1 }""", it) + assertEquals(-100, decoded.count) + } + + @Test + fun testExponentDecodingPositiveDouble() = parametrizedTest { + val decoded = Json.decodeFromString<SomeDataDouble>("""{ "count": 1.5E1 }""", it) + assertEquals(15.0, decoded.count) + } + + @Test + fun testExponentDecodingNegativeDouble() = parametrizedTest { + val decoded = Json.decodeFromString<SomeDataDouble>("""{ "count": -1e-1 }""", it) + assertEquals(-0.1, decoded.count) + } + + @Test + fun testExponentDecodingErrorTruncatedDecimal() = parametrizedTest { + assertFailsWithSerial("JsonDecodingException") + { Json.decodeFromString<SomeData>("""{ "count": -1E-1 }""", it) } + } + + @Test + fun testExponentDecodingErrorExponent() = parametrizedTest { + assertFailsWithSerial("JsonDecodingException") + { Json.decodeFromString<SomeData>("""{ "count": 1e-1e-1 }""", it) } + } + + @Test + fun testExponentDecodingErrorExponentDouble() = parametrizedTest { + assertFailsWithSerial("JsonDecodingException") + { Json.decodeFromString<SomeDataDouble>("""{ "count": 1e-1e-1 }""", it) } + } + + @Test + fun testExponentOverflowDouble() = parametrizedTest { + assertFailsWithSerial("JsonDecodingException") + { Json.decodeFromString<SomeDataDouble>("""{ "count": 10000e10000 }""", it) } + } + + @Test + fun testExponentUnderflowDouble() = parametrizedTest { + assertFailsWithSerial("JsonDecodingException") + { Json.decodeFromString<SomeDataDouble>("""{ "count": -100e2222 }""", it) } + } + + @Test + fun testExponentOverflow() = parametrizedTest { + assertFailsWithSerial("JsonDecodingException") + { Json.decodeFromString<SomeData>("""{ "count": 10000e10000 }""", it) } + } + + @Test + fun testExponentUnderflow() = parametrizedTest { + assertFailsWithSerial("JsonDecodingException") + { Json.decodeFromString<SomeData>("""{ "count": -10000e10000 }""", it) } + } +}
\ No newline at end of file diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonGenericTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonGenericTest.kt new file mode 100644 index 00000000..c4618086 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonGenericTest.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlin.test.* + +class JsonGenericTest : JsonTestBase() { + + @Serializable + class Array2DBox(val arr: Array<Array<Double>>) { + override fun toString(): String { + return arr.contentDeepToString() + } + } + + @Test + fun testWriteDefaultPair() = parametrizedTest { jsonTestingMode -> + val pair = 42 to "foo" + val serializer = PairSerializer( + Int.serializer(), + String.serializer() + ) + val s = default.encodeToString(serializer, pair, jsonTestingMode) + assertEquals("""{"first":42,"second":"foo"}""", s) + val restored = default.decodeFromString(serializer, s, jsonTestingMode) + assertEquals(pair, restored) + } + + @Test + fun testWritePlainTriple() = parametrizedTest { jsonTestingMode -> + val triple = Triple(42, "foo", false) + val serializer = TripleSerializer( + Int.serializer(), + String.serializer(), + Boolean.serializer() + ) + val s = default.encodeToString(serializer, triple, jsonTestingMode) + assertEquals("""{"first":42,"second":"foo","third":false}""", s) + val restored = default.decodeFromString(serializer, s, jsonTestingMode) + assertEquals(triple, restored) + } + + @Test + fun testRecursiveArrays() = parametrizedTest { jsonTestingMode -> + val arr = Array2DBox(arrayOf(arrayOf(2.1, 1.2), arrayOf(42.3, -3.4))) + val str = default.encodeToString(Array2DBox.serializer(), arr, jsonTestingMode) + assertEquals("""{"arr":[[2.1,1.2],[42.3,-3.4]]}""", str) + val restored = default.decodeFromString(Array2DBox.serializer(), str, jsonTestingMode) + assertTrue(arr.arr.contentDeepEquals(restored.arr)) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonHugeDataSerializationTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonHugeDataSerializationTest.kt new file mode 100644 index 00000000..0a633268 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonHugeDataSerializationTest.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.Serializable +import kotlin.test.Test + +class JsonHugeDataSerializationTest : JsonTestBase() { + + @Serializable + private data class Node( + val children: List<Node> + ) + + private fun createNodes(count: Int, depth: Int): List<Node> { + val ret = mutableListOf<Node>() + if (depth == 0) return ret + for (i in 0 until count) { + ret.add(Node(createNodes(1, depth - 1))) + } + return ret + } + + @Test + fun test() { + // create some huge instance + val rootNode = Node(createNodes(1000, 10)) + + val expectedJson = Json.encodeToString(Node.serializer(), rootNode) + + /* + The assertJsonFormAndRestored function, when checking the encoding, will call Json.encodeToString(...) for `JsonTestingMode.STREAMING` + since the string `expectedJson` was generated by the same function, the test will always consider + the encoding to the `STREAMING` mode is correct, even if there was actually an error there. So only TREE, JAVA_STREAMS and OKIO are actually being tested here + */ + assertJsonFormAndRestored(Node.serializer(), rootNode, expectedJson) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonImplicitNullsTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonImplicitNullsTest.kt new file mode 100644 index 00000000..c06c058c --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonImplicitNullsTest.kt @@ -0,0 +1,13 @@ +package kotlinx.serialization.json + +import kotlinx.serialization.* + +class JsonImplicitNullsTest: AbstractJsonImplicitNullsTest() { + override fun <T> Json.encode(value: T, serializer: KSerializer<T>): String { + return encodeToString(serializer, value) + } + + override fun <T> Json.decode(json: String, serializer: KSerializer<T>): T { + return decodeFromString(serializer, json) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonMapKeysTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonMapKeysTest.kt new file mode 100644 index 00000000..560e51fe --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonMapKeysTest.kt @@ -0,0 +1,177 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.test.* +import kotlin.jvm.* +import kotlin.test.* + +@JvmInline +@Serializable +value class ComplexCarrier(val c: IntData) + +@JvmInline +@Serializable +value class PrimitiveCarrier(val c: String) + +data class ContextualValue(val c: String) { + companion object: KSerializer<ContextualValue> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ContextualValue", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: ContextualValue) { + encoder.encodeString(value.c) + } + + override fun deserialize(decoder: Decoder): ContextualValue { + return ContextualValue(decoder.decodeString()) + } + } +} + +class JsonMapKeysTest : JsonTestBase() { + @Serializable + private data class WithMap(val map: Map<Long, Long>) + + @Serializable + private data class WithBooleanMap(val map: Map<Boolean, Boolean>) + + @Serializable + private data class WithValueKeyMap(val map: Map<PrimitiveCarrier, Long>) + + @Serializable + private data class WithEnum(val map: Map<SampleEnum, Long>) + + @Serializable + private data class WithComplexKey(val map: Map<IntData, String>) + + @Serializable + private data class WithComplexValueKey(val map: Map<ComplexCarrier, String>) + + @Serializable + private data class WithContextualValueKey(val map: Map<@Contextual PrimitiveCarrier, Long>) + + @Serializable + private data class WithContextualKey(val map: Map<@Contextual ContextualValue, Long>) + + @Test + fun testMapKeysSupportNumbers() = parametrizedTest { + assertStringFormAndRestored( + """{"map":{"10":10,"20":20}}""", + WithMap(mapOf(10L to 10L, 20L to 20L)), + WithMap.serializer(), + default + ) + } + + @Test + fun testMapKeysSupportBooleans() = parametrizedTest { + assertStringFormAndRestored( + """{"map":{"true":false,"false":true}}""", + WithBooleanMap(mapOf(true to false, false to true)), + WithBooleanMap.serializer(), + default + ) + } + + // As a result of quoting ignorance when parsing primitives, it is possible to parse unquoted maps if Kotlin keys are non-string primitives. + // This is not spec-compliant, but I do not see any problems with it. + @Test + fun testMapDeserializesUnquotedKeys() = parametrizedTest { + assertEquals(WithMap(mapOf(10L to 10L, 20L to 20L)), default.decodeFromString("""{"map":{10:10,20:20}}""")) + assertEquals( + WithBooleanMap(mapOf(true to false, false to true)), + default.decodeFromString("""{"map":{true:false,false:true}}""") + ) + assertFailsWithSerial("JsonDecodingException") { + default.decodeFromString( + MapSerializer( + String.serializer(), + Boolean.serializer() + ),"""{"map":{true:false,false:true}}""" + ) + } + } + + @Test + fun testStructuredMapKeysShouldBeProhibitedByDefault() = parametrizedTest { streaming -> + verifyProhibition(WithComplexKey(mapOf(IntData(42) to "42")), streaming) + verifyProhibition(WithComplexValueKey(mapOf(ComplexCarrier(IntData(42)) to "42")), streaming) + } + + private inline fun <reified T: Any> verifyProhibition(value: T, streaming: JsonTestingMode) { + assertFailsWithSerialMessage("JsonEncodingException", "can't be used in JSON as a key in the map") { + Json.encodeToString(value, streaming) + } + } + + @Test + fun testStructuredMapKeysAllowedWithFlag() = assertJsonFormAndRestored( + WithComplexKey.serializer(), + WithComplexKey(mapOf(IntData(42) to "42")), + """{"map":[{"intV":42},"42"]}""", + Json { allowStructuredMapKeys = true } + ) + + @Test + fun testStructuredValueMapKeysAllowedWithFlag() { + assertJsonFormAndRestored( + WithComplexValueKey.serializer(), + WithComplexValueKey(mapOf(ComplexCarrier(IntData(42)) to "42")), + """{"map":[{"intV":42},"42"]}""", + Json { allowStructuredMapKeys = true } + ) + } + + @Test + fun testEnumsAreAllowedAsMapKeys() = assertJsonFormAndRestored( + WithEnum.serializer(), + WithEnum(mapOf(SampleEnum.OptionA to 1L, SampleEnum.OptionC to 3L)), + """{"map":{"OptionA":1,"OptionC":3}}""", + Json + ) + + @Test + fun testPrimitivesAreAllowedAsValueMapKeys() { + assertJsonFormAndRestored( + WithValueKeyMap.serializer(), + WithValueKeyMap(mapOf(PrimitiveCarrier("fooKey") to 1)), + """{"map":{"fooKey":1}}""", + Json + ) + } + + @Test + fun testContextualValuePrimitivesAreAllowedAsValueMapKeys() { + assertJsonFormAndRestored( + WithContextualValueKey.serializer(), + WithContextualValueKey(mapOf(PrimitiveCarrier("fooKey") to 1)), + """{"map":{"fooKey":1}}""", + Json { + serializersModule = + SerializersModule { contextual(PrimitiveCarrier::class, PrimitiveCarrier.serializer()) } + } + ) + } + + @Test + fun testContextualPrimitivesAreAllowedAsValueMapKeys() { + assertJsonFormAndRestored( + WithContextualKey.serializer(), + WithContextualKey(mapOf(ContextualValue("fooKey") to 1)), + """{"map":{"fooKey":1}}""", + Json { + serializersModule = SerializersModule { contextual(ContextualValue::class, ContextualValue) } + } + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonModesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonModesTest.kt new file mode 100644 index 00000000..e7f107c8 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonModesTest.kt @@ -0,0 +1,128 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class JsonModesTest : JsonTestBase() { + + @Test + fun testNan() = parametrizedTest(lenient) { + assertStringFormAndRestored("{\"double\":NaN,\"float\":NaN}", Box(Double.NaN, Float.NaN), Box.serializer()) + } + + @Test + fun testInfinity() = parametrizedTest(lenient) { + assertStringFormAndRestored( + "{\"double\":Infinity,\"float\":-Infinity}", + Box(Double.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY), + Box.serializer() + ) + } + + @Test + fun nonStrictJsonCanSkipValues() = parametrizedTest { jsonTestingMode -> + val data = JsonOptionalTests.Data() + assertEquals( + lenient.decodeFromString(JsonOptionalTests.Data.serializer(), "{strangeField: 100500, a:0}", jsonTestingMode), + data + ) + assertEquals( + lenient.decodeFromString(JsonOptionalTests.Data.serializer(), "{a:0, strangeField: 100500}", jsonTestingMode), + data + ) + } + + @Test + fun nonStrictJsonCanSkipComplexValues() = parametrizedTest { jsonTestingMode -> + val data = JsonOptionalTests.Data() + + assertEquals( + lenient.decodeFromString( + JsonOptionalTests.Data.serializer(), + "{a: 0, strangeField: {a: b, c: {d: e}, f: [g,h,j] }}", + jsonTestingMode + ), + data + ) + assertEquals( + lenient.decodeFromString( + JsonOptionalTests.Data.serializer(), + "{strangeField: {a: b, c: {d: e}, f: [g,h,j] }, a: 0}", + jsonTestingMode + ), + data + ) + } + + @Test + fun ignoreKeysCanIgnoreWeirdStringValues() { + val data = JsonOptionalTests.Data() + fun doTest(input: String) { + assertEquals(data, lenient.decodeFromString(input)) + } + doTest("{a: 0, strangeField: [\"imma string with } bracket\", \"sss\"]}") + doTest("{a: 0, strangeField: [\"imma string with ] bracket\", \"sss\"]}") + doTest("{a: 0, strangeField: \"imma string with } bracket\"}") + doTest("{a: 0, strangeField: \"imma string with ] bracket\"}") + doTest("{a: 0, strangeField: {key: \"imma string with ] bracket\"}}") + doTest("{a: 0, strangeField: {key: \"imma string with } bracket\"}}") + doTest("""{"a": 0, "strangeField": {"key": "imma string with } bracket"}}""") + doTest("""{"a": 0, "strangeField": {"key": "imma string with ] bracket"}}""") + doTest("""{"a": 0, "strangeField": ["imma string with ] bracket"]}""") + doTest("""{"a": 0, "strangeField": ["imma string with } bracket"]}""") + } + + @Serializable + class Empty + + @Test + fun lenientThrowOnMalformedString() { + fun doTest(input: String) { + assertFailsWith<SerializationException> { lenient.decodeFromString(Empty.serializer(), input) } + } + doTest("""{"a":[{"b":[{"c":{}d",""e"":"}]}""") + doTest("""{"a":[}""") + doTest("""{"a":""") + lenient.decodeFromString(Empty.serializer(), """{"a":[]}""") // should not throw + } + + @Test + fun testSerializeQuotedJson() = parametrizedTest { jsonTestingMode -> + assertEquals( + """{"a":10,"e":false,"c":"Hello"}""", default.encodeToString( + JsonTransientTest.Data.serializer(), + JsonTransientTest.Data(10, 100), jsonTestingMode + ) + ) + } + + @Test + fun testStrictJsonCanNotSkipValues() = parametrizedTest { jsonTestingMode -> + assertFailsWith(SerializationException::class) { + default.decodeFromString(JsonOptionalTests.Data.serializer(), "{strangeField: 100500, a:0}", jsonTestingMode) + } + } + + @Serializable + data class Box(val double: Double, val float: Float) + + + @Serializable + object Object + + @Serializable + data class Holder(val o: Object) + + @Test + fun testIgnoreUnknownKeysObject() = parametrizedTest { jsonTestingMode -> + assertEquals(Holder(Object), lenient.decodeFromString("""{"o":{}}""", jsonTestingMode)) + assertEquals(Holder(Object), lenient.decodeFromString("""{"o":{"unknown":{"b":"c"}}}""", jsonTestingMode)) + assertEquals(Object, lenient.decodeFromString("""{}""", jsonTestingMode)) + assertEquals(Object, lenient.decodeFromString("""{"o":{"unknown":{"b":"c"}}}""", jsonTestingMode)) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonNumericKeysTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonNumericKeysTest.kt new file mode 100644 index 00000000..ee3e8f15 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonNumericKeysTest.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlinx.serialization.serializer +import kotlin.test.* + +class JsonNumericKeysTest : JsonTestBase() { + @Serializable + data class EntryWrapper(val e: Map.Entry<Int, Int>) + + @Serializable + data class MapWrapper(val m: Map<Int, Int>) + + @Test + fun testIntegerKeyInTopLevelEntry() { + assertJsonFormAndRestored(MapEntrySerializer(Int.serializer(), Int.serializer()), getEntry(), """{"1":2}""") + } + + @Test + fun testIntegerKeyInEntry() { + assertJsonFormAndRestored(EntryWrapper.serializer(), EntryWrapper(getEntry()), """{"e":{"1":2}}""") + } + + @Test + fun testIntegerKeyInTopLevelMap() = parametrizedTest { + assertJsonFormAndRestored(serializer(), mapOf(1 to 2), """{"1":2}""") + + } + + @Test + fun testIntegerKeyInMap() = parametrizedTest { + assertJsonFormAndRestored(MapWrapper.serializer(), MapWrapper(mapOf(1 to 2)), """{"m":{"1":2}}""") + } + + // Workaround equals on JS and Native + fun getEntry(): Map.Entry<Int, Int> { + val e = default.decodeFromString(MapEntrySerializer(Int.serializer(), Int.serializer()), """{"1":2}""") + assertEquals(1, e.key) + assertEquals(2, e.value) + return e + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonOptionalTests.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonOptionalTests.kt new file mode 100644 index 00000000..679a972b --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonOptionalTests.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class JsonOptionalTests : JsonTestBase() { + + @Suppress("EqualsOrHashCode") + @Serializable + internal class Data(@Required val a: Int = 0, val b: Int = 42) { + + var c = "Hello" + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as Data + + if (a != other.a) return false + if (b != other.b) return false + if (c != other.c) return false + + return true + } + } + + @Test + fun testAll() = parametrizedTest { jsonTestingMode -> + assertEquals("""{"a":0,"b":42,"c":"Hello"}""", + default.encodeToString(Data.serializer(), Data(), jsonTestingMode)) + assertEquals(lenient.decodeFromString(Data.serializer(), "{a:0,b:43,c:Hello}", jsonTestingMode), Data(b = 43)) + assertEquals(lenient.decodeFromString(Data.serializer(), "{a:0,b:42,c:Hello}", jsonTestingMode), Data()) + } + + @Test + fun testMissingOptionals() = parametrizedTest { jsonTestingMode -> + assertEquals(default.decodeFromString(Data.serializer(), """{"a":0,"c":"Hello"}""", jsonTestingMode), Data()) + assertEquals(default.decodeFromString(Data.serializer(), """{"a":0}""", jsonTestingMode), Data()) + } + + @Test + fun testThrowMissingField() = parametrizedTest { jsonTestingMode -> + assertFailsWithMissingField { + lenient.decodeFromString(Data.serializer(), "{b:0}", jsonTestingMode) + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonParserFailureModesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonParserFailureModesTest.kt new file mode 100644 index 00000000..892696b8 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonParserFailureModesTest.kt @@ -0,0 +1,160 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class JsonParserFailureModesTest : JsonTestBase() { + + @Serializable + data class Holder( + val id: Long + ) + + @Test + fun testFailureModes() = parametrizedTest { + assertFailsWithSerial("JsonDecodingException") { + default.decodeFromString( + Holder.serializer(), + """{"id": "}""", + it + ) + } + assertFailsWithSerial("JsonDecodingException") { + default.decodeFromString( + Holder.serializer(), + """{"id": ""}""", + it + ) + } + assertFailsWithSerial("JsonDecodingException") { + default.decodeFromString( + Holder.serializer(), + """{"id":a}""", + it + ) + } + assertFailsWithSerial("JsonDecodingException") { + default.decodeFromString( + Holder.serializer(), + """{"id":2.0}""", + it + ) + } + assertFailsWithSerial("JsonDecodingException") { + default.decodeFromString( + Holder.serializer(), + """{"id2":2}""", + it + ) + } + // 9223372036854775807 is Long.MAX_VALUE + assertFailsWithSerial("JsonDecodingException") { + default.decodeFromString( + Holder.serializer(), + """{"id":${Long.MAX_VALUE}""" + "00" + "}", + it + ) + } + // -9223372036854775808 is Long.MIN_VALUE + assertFailsWithSerial("JsonDecodingException") { + default.decodeFromString( + Holder.serializer(), + """{"id":9223372036854775808}""", + it + ) + } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString(Holder.serializer(), """{"id"}""", it) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString(Holder.serializer(), """{"id}""", it) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString(Holder.serializer(), """{"i}""", it) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString(Holder.serializer(), """{"}""", it) } + assertFailsWithMissingField { default.decodeFromString(Holder.serializer(), """{}""", it) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString(Holder.serializer(), """{""", it) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString(Holder.serializer(), """}""", it) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString(Holder.serializer(), """{""", it) } + } + + @Serializable + class BooleanHolder(val b: Boolean) + + @Test + fun testBoolean() = parametrizedTest { + assertFailsWithSerial("JsonDecodingException") { + default.decodeFromString( + BooleanHolder.serializer(), + """{"b": fals}""", + it + ) + } + assertFailsWithSerial("JsonDecodingException") { + default.decodeFromString( + BooleanHolder.serializer(), + """{"b": 123}""", + it + ) + } + } + + @Serializable + class PrimitiveHolder( + val b: Byte = 0, val s: Short = 0, val i: Int = 0 + ) + + @Test + fun testOverflow() = parametrizedTest { + // Byte overflow + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString<PrimitiveHolder>("""{"b": 128}""", it) } + // Short overflow + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString<PrimitiveHolder>("""{"s": 32768}""", it) } + // Int overflow + assertFailsWithSerial("JsonDecodingException") { + default.decodeFromString<PrimitiveHolder>( + """{"i": 2147483648}""", + it + ) + } + } + + @Test + fun testNoOverflow() = parametrizedTest { + default.decodeFromString<PrimitiveHolder>("""{"b": ${Byte.MAX_VALUE}}""", it) + default.decodeFromString<PrimitiveHolder>("""{"b": ${Byte.MIN_VALUE}}""", it) + default.decodeFromString<PrimitiveHolder>("""{"s": ${Short.MAX_VALUE}}""", it) + default.decodeFromString<PrimitiveHolder>("""{"s": ${Short.MIN_VALUE}}""", it) + default.decodeFromString<PrimitiveHolder>("""{"i": ${Int.MAX_VALUE}}""", it) + default.decodeFromString<PrimitiveHolder>("""{"i": ${Int.MIN_VALUE}}""", it) + default.decodeFromString<Holder>("""{"id": ${Long.MIN_VALUE.toString()}}""", it) + default.decodeFromString<Holder>("""{"id": ${Long.MAX_VALUE.toString()}}""", it) + } + + @Test + fun testInvalidNumber() = parametrizedTest { + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString<Holder>("""{"id":-}""", it) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString<Holder>("""{"id":+}""", it) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString<Holder>("""{"id":--}""", it) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString<Holder>("""{"id":1-1}""", it) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString<Holder>("""{"id":0-1}""", it) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString<Holder>("""{"id":0-}""", it) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString<Holder>("""{"id":a}""", it) } + assertFailsWithSerial("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 { + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString<BooleanWrapper>("""{"b":{"b":"b"}}""", it) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString<BooleanWrapper>("""{"b":null}""", it) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString<StringWrapper>("""{"s":{"s":"s"}}""", it) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString<StringWrapper>("""{"s":null}""", it) } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonParserTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonParserTest.kt new file mode 100644 index 00000000..94f7052c --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonParserTest.kt @@ -0,0 +1,116 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlinx.serialization.json.internal.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class JsonParserTest : JsonTestBase() { + + @Test + fun testQuotedBrace() { + val tree = parse("""{"x": "{"}""") + assertTrue("x" in tree) + assertEquals("{", (tree.getValue("x") as JsonPrimitive).content) + } + + private fun parse(input: String) = default.parseToJsonElement(input).jsonObject + + @Test + fun testEmptyKey() { + val tree = parse("""{"":"","":""}""") + assertTrue("" in tree) + assertEquals("", (tree.getValue("") as JsonPrimitive).content) + } + + @Test + fun testEmptyValue() { + assertFailsWithSerial("JsonDecodingException") { + parse("""{"X": "foo", "Y"}""") + } + } + + @Test + fun testIncorrectUnicodeEscape() { + assertFailsWithSerial("JsonDecodingException") { + parse("""{"X": "\uDD1H"}""") + } + } + + @Test + fun testParseEscapedSymbols() { + assertEquals( + StringData("https://t.co/M1uhwigsMT"), + default.decodeFromString(StringData.serializer(), """{"data":"https:\/\/t.co\/M1uhwigsMT"}""") + ) + assertEquals(StringData("\"test\""), default.decodeFromString(StringData.serializer(), """{"data": "\"test\""}""")) + assertEquals(StringData("\u00c9"), default.decodeFromString(StringData.serializer(), """{"data": "\u00c9"}""")) + assertEquals(StringData("""\\"""), default.decodeFromString(StringData.serializer(), """{"data": "\\\\"}""")) + } + + @Test + fun testWorkWithNonAsciiSymbols() { + assertStringFormAndRestored( + """{"data":"Русские Буквы 🤔"}""", + StringData("Русские Буквы \uD83E\uDD14"), + StringData.serializer() + ) + } + + @Test + fun testUnicodeEscapes() { + val data = buildString { + append(1.toChar()) + append(".") + append(0x20.toChar()) + append(".") + append("\n") + } + + assertJsonFormAndRestored(String.serializer(), data, "\"\\u0001. .\\n\"") + } + + @Test + fun testTrailingComma() { + testTrailingComma("{\"id\":0,}") + testTrailingComma("{\"id\":0 ,}") + testTrailingComma("{\"id\":0 , ,}") + } + + private fun testTrailingComma(content: String) { + assertFailsWithSerialMessage("JsonDecodingException", "Trailing comma before the end of JSON object") { Json.parseToJsonElement(content) } + } + + @Test + fun testUnclosedStringLiteral() { + assertFailsWithSerial("JsonDecodingException") { + parse("\"") + } + + assertFailsWithSerial("JsonDecodingException") { + parse("""{"id":"""") + } + } + + @Test + fun testNullValue() { + val obj = Json.parseToJsonElement("""{"k":null}""").jsonObject + val value = obj["k"]!! + assertTrue { value is JsonNull } + assertFalse { value.jsonPrimitive.isString } + } + + @Test + fun testNullStringValue() { + val obj = Json.parseToJsonElement("""{"k":"null"}""").jsonObject + val value = obj["k"]!! + assertFalse { value is JsonNull } + assertTrue { value.jsonPrimitive.isString } + assertEquals("null", obj["k"]!!.jsonPrimitive.content) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonPrettyPrintTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonPrettyPrintTest.kt new file mode 100644 index 00000000..8283e25b --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonPrettyPrintTest.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlin.test.* + +class JsonPrettyPrintTest : JsonTestBase() { + val fmt = Json(default) { prettyPrint = true; encodeDefaults = true } + + @Serializable + class Empty + + @Serializable + class A(val empty: Empty = Empty()) + + @Serializable + class B(val prefix: String = "a", val empty: Empty = Empty(), val postfix: String = "b") + + @Serializable + class Recursive(val rec: Recursive?, val empty: Empty = Empty()) + + @Serializable + class WithListRec(val rec: WithListRec?, val l: List<Int> = listOf()) + + @Serializable + class WithDefaults(val x: String = "x", val y: Int = 0) + + @Test + fun testTopLevel() = parametrizedTest { mode -> + assertEquals("{}", fmt.encodeToString(Empty(), mode)) + } + + @Test + fun testWithDefaults() = parametrizedTest { mode -> + val dropDefaults = Json(fmt) { encodeDefaults = false } + val s = "{\n \"boxed\": {}\n}" + assertEquals(s, dropDefaults.encodeToString(Box(WithDefaults()), mode)) + } + + @Test + fun testPlain() = parametrizedTest { mode -> + val s = """{ + | "empty": {} + |}""".trimMargin() + assertEquals(s, fmt.encodeToString(A(), mode)) + } + + @Test + fun testInside() = parametrizedTest { mode -> + val s = """{ + | "prefix": "a", + | "empty": {}, + | "postfix": "b" + |}""".trimMargin() + assertEquals(s, fmt.encodeToString(B(), mode)) + } + + @Test + fun testRecursive() = parametrizedTest { mode -> + val obj = Recursive(Recursive(null)) + val s = "{\n \"rec\": {\n \"rec\": null,\n \"empty\": {}\n },\n \"empty\": {}\n}" + assertEquals(s, fmt.encodeToString(obj, mode)) + } + + @Test + fun test() = parametrizedTest { mode -> + val obj = WithListRec(WithListRec(null), listOf(1, 2, 3)) + val s = + "{\n \"rec\": {\n \"rec\": null,\n \"l\": []\n },\n \"l\": [\n 1,\n 2,\n 3\n ]\n}" + assertEquals(s, fmt.encodeToString(obj, mode)) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonReifiedCollectionsTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonReifiedCollectionsTest.kt new file mode 100644 index 00000000..3fc62489 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonReifiedCollectionsTest.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlin.test.* + +class JsonReifiedCollectionsTest : JsonTestBase() { + @Serializable + data class DataHolder(val data: String) + + @Test + fun testReifiedList() = parametrizedTest { jsonTestingMode -> + val data = listOf(DataHolder("data"), DataHolder("not data")) + val json = default.encodeToString(data, jsonTestingMode) + val data2 = default.decodeFromString<List<DataHolder>>(json, jsonTestingMode) + assertEquals(data, data2) + } + + @Test + fun testReifiedMap() = parametrizedTest { jsonTestingMode -> + val data = mapOf("data" to DataHolder("data"), "smth" to DataHolder("not data")) + val json = lenient.encodeToString(data, jsonTestingMode) + val data2 = lenient.decodeFromString<Map<String, DataHolder>>(json, jsonTestingMode) + assertEquals(data, data2) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonRootLevelNullTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonRootLevelNullTest.kt new file mode 100644 index 00000000..69397764 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonRootLevelNullTest.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.* +import kotlin.test.* + +class JsonRootLevelNullTest : JsonTestBase() { + + @Serializable + private data class Simple(val a: Int = 42) + + @Test + fun testNullableEncode() { + // Top-level nulls in tagged encoder is not yet supported, no parametrized test + val obj: Simple? = null + val json = default.encodeToString(Simple.serializer().nullable, obj) + assertEquals("null", json) + } + + @Test + fun testNullableDecode() = parametrizedTest { jsonTestingMode -> + val result = default.decodeFromString(Simple.serializer().nullable, "null", jsonTestingMode) + assertNull(result) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonSealedSubclassTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonSealedSubclassTest.kt new file mode 100644 index 00000000..5fac878d --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonSealedSubclassTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlin.test.Test +import kotlin.test.assertEquals + +sealed class Expr + +@Serializable +data class Var(val id: String) : Expr() + +class JsonSealedSubclassTest : JsonTestBase() { + + // inspired by kotlinx.serialization/#112 + @Test + fun testCallSuperSealedConstructorProperly() = parametrizedTest { jsonTestingMode -> + val v1 = Var("a") + val s1 = default.encodeToString(Var.serializer(), v1, jsonTestingMode)// {"id":"a"} + assertEquals("""{"id":"a"}""", s1) + val v2: Var = default.decodeFromString(Var.serializer(), s1, JsonTestingMode.STREAMING) // should not throw IllegalAccessError + assertEquals(v1, v2) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTestBase.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTestBase.kt new file mode 100644 index 00000000..6f3b132e --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTestBase.kt @@ -0,0 +1,176 @@ +/* + * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.json.internal.* +import kotlinx.serialization.json.okio.decodeFromBufferedSource +import kotlinx.serialization.json.okio.encodeToBufferedSink +import kotlinx.serialization.modules.EmptySerializersModule +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.test.* +import kotlin.test.assertEquals +import okio.* +import kotlin.test.assertTrue + + +enum class JsonTestingMode { + STREAMING, + TREE, + OKIO_STREAMS, + JAVA_STREAMS; + + companion object { + fun value(i: Int) = values()[i] + } +} + +abstract class JsonTestBase { + protected val default = Json { encodeDefaults = true } + protected val lenient = Json { isLenient = true; ignoreUnknownKeys = true; allowSpecialFloatingPointValues = true } + + internal inline fun <reified T : Any> Json.encodeToString(value: T, jsonTestingMode: JsonTestingMode): String { + val serializer = serializersModule.serializer<T>() + return encodeToString(serializer, value, jsonTestingMode) + } + + internal fun <T> Json.encodeToString( + serializer: SerializationStrategy<T>, + value: T, + jsonTestingMode: JsonTestingMode + ): String = + when (jsonTestingMode) { + JsonTestingMode.STREAMING -> { + encodeToString(serializer, value) + } + JsonTestingMode.JAVA_STREAMS -> { + encodeViaStream(serializer, value) + } + JsonTestingMode.TREE -> { + val tree = writeJson(this, value, serializer) + encodeToString(tree) + } + JsonTestingMode.OKIO_STREAMS -> { + val buffer = Buffer() + encodeToBufferedSink(serializer, value, buffer) + buffer.readUtf8() + } + } + + internal inline fun <reified T : Any> Json.decodeFromString(source: String, jsonTestingMode: JsonTestingMode): T { + val deserializer = serializersModule.serializer<T>() + return decodeFromString(deserializer, source, jsonTestingMode) + } + + internal fun <T> Json.decodeFromString( + deserializer: DeserializationStrategy<T>, + source: String, + jsonTestingMode: JsonTestingMode + ): T = + when (jsonTestingMode) { + JsonTestingMode.STREAMING -> { + decodeFromString(deserializer, source) + } + JsonTestingMode.JAVA_STREAMS -> { + decodeViaStream(deserializer, source) + } + JsonTestingMode.TREE -> { + val tree = decodeStringToJsonTree(this, deserializer, source) + readJson(this, tree, deserializer) + } + JsonTestingMode.OKIO_STREAMS -> { + val buffer = Buffer() + buffer.writeUtf8(source) + decodeFromBufferedSource(deserializer, buffer) + } + } + + protected open fun parametrizedTest(test: (JsonTestingMode) -> Unit) { + processResults(buildList { + add(runCatching { test(JsonTestingMode.STREAMING) }) + add(runCatching { test(JsonTestingMode.TREE) }) + add(runCatching { test(JsonTestingMode.OKIO_STREAMS) }) + + if (isJvm()) { + add(runCatching { test(JsonTestingMode.JAVA_STREAMS) }) + } + }) + } + + private inner class SwitchableJson( + val json: Json, + val jsonTestingMode: JsonTestingMode, + override val serializersModule: SerializersModule = EmptySerializersModule() + ) : StringFormat { + override fun <T> encodeToString(serializer: SerializationStrategy<T>, value: T): String { + return json.encodeToString(serializer, value, jsonTestingMode) + } + + override fun <T> decodeFromString(deserializer: DeserializationStrategy<T>, string: String): T { + return json.decodeFromString(deserializer, string, jsonTestingMode) + } + } + + protected fun parametrizedTest(json: Json, test: StringFormat.() -> Unit) { + val streamingResult = runCatching { SwitchableJson(json, JsonTestingMode.STREAMING).test() } + val treeResult = runCatching { SwitchableJson(json, JsonTestingMode.TREE).test() } + val okioResult = runCatching { SwitchableJson(json, JsonTestingMode.OKIO_STREAMS).test() } + processResults(listOf(streamingResult, treeResult, okioResult)) + } + + protected fun processResults(results: List<Result<*>>) { + results.forEachIndexed { i, result -> + result.onFailure { + println("Failed test for ${JsonTestingMode.value(i)}") + throw it + } + } + for (i in results.indices) { + for (j in results.indices) { + if (i == j) continue + assertEquals( + results[i].getOrNull()!!, + results[j].getOrNull()!!, + "Results differ for ${JsonTestingMode.value(i)} and ${JsonTestingMode.value(j)}" + ) + } + } + } + + /** + * Same as [assertStringFormAndRestored], but tests both json converters (streaming and tree) + * via [parametrizedTest] + */ + internal fun <T> assertJsonFormAndRestored( + serializer: KSerializer<T>, + data: T, + expected: String, + json: Json = default + ) { + parametrizedTest { jsonTestingMode -> + val serialized = json.encodeToString(serializer, data, jsonTestingMode) + assertEquals(expected, serialized, "Failed with streaming = $jsonTestingMode") + val deserialized: T = json.decodeFromString(serializer, serialized, jsonTestingMode) + assertEquals(data, deserialized, "Failed with streaming = $jsonTestingMode") + } + } + /** + * Same as [assertStringFormAndRestored], but tests both json converters (streaming and tree) + * via [parametrizedTest]. Use custom checker for deserialized value. + */ + internal fun <T> assertJsonFormAndRestoredCustom( + serializer: KSerializer<T>, + data: T, + expected: String, + check: (T, T) -> Boolean + ) { + parametrizedTest { jsonTestingMode -> + val serialized = Json.encodeToString(serializer, data, jsonTestingMode) + assertEquals(expected, serialized, "Failed with streaming = $jsonTestingMode") + val deserialized: T = Json.decodeFromString(serializer, serialized, jsonTestingMode) + assertTrue("Failed with streaming = $jsonTestingMode\n\tsource value =$data\n\tdeserialized value=$deserialized") { check(data, deserialized) } + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTransformingSerializerTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTransformingSerializerTest.kt new file mode 100644 index 00000000..516587f5 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTransformingSerializerTest.kt @@ -0,0 +1,101 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlin.test.* + +class JsonTransformingSerializerTest : JsonTestBase() { + val json = Json { encodeDefaults = false } + + @Serializable + data class Example( + val name: String, + @Serializable(UnwrappingJsonListSerializer::class) val data: StringData, + @SerialName("more_data") @Serializable(WrappingJsonListSerializer::class) val moreData: List<StringData> = emptyList() + ) + + object WrappingJsonListSerializer : + JsonTransformingSerializer<List<StringData>>(ListSerializer(StringData.serializer())) { + override fun transformDeserialize(element: JsonElement): JsonElement = + if (element !is JsonArray) JsonArray(listOf(element)) else element + } + + object UnwrappingJsonListSerializer : + JsonTransformingSerializer<StringData>(StringData.serializer()) { + override fun transformDeserialize(element: JsonElement): JsonElement { + if (element !is JsonArray) return element + require(element.size == 1) { "Array size must be equal to 1 to unwrap it" } + return element.first() + } + } + + object DroppingNameSerializer : JsonTransformingSerializer<Example>(Example.serializer()) { + override fun transformSerialize(element: JsonElement): JsonElement = + JsonObject(element.jsonObject.filterNot { (k, v) -> k == "name" && v.jsonPrimitive.content == "First" }) + } + + @Test + fun testExampleCanBeParsed() = parametrizedTest { streaming -> + val testDataInput = listOf( + """{"name":"test","data":{"data":"str1"},"more_data":[{"data":"str2"}]}""", + """{"name":"test","data":{"data":"str1"},"more_data":{"data":"str2"}}""", + """{"name":"test","data":[{"data":"str1"}],"more_data":[{"data":"str2"}]}""", + """{"name":"test","data":[{"data":"str1"}],"more_data":{"data":"str2"}}""" + ) + val goldenVal = Example("test", StringData("str1"), listOf(StringData("str2"))) + + + for (i in testDataInput.indices) { + assertEquals( + goldenVal, + json.decodeFromString(Example.serializer(), testDataInput[i], streaming), + "failed test on ${testDataInput[i]}, jsonTestingMode = $streaming" + ) + } + } + + @Test + fun testExampleDroppingNameSerializer() = parametrizedTest { streaming -> + val testDataInput = listOf( + Example("First", StringData("str1")), + Example("Second", StringData("str1")) + ) + + val goldenVals = listOf( + """{"data":{"data":"str1"}}""", + """{"name":"Second","data":{"data":"str1"}}""" + ) + for (i in testDataInput.indices) { + assertEquals( + goldenVals[i], + json.encodeToString(DroppingNameSerializer, testDataInput[i], streaming), + "failed test on ${testDataInput[i]}, jsonTestingMode = $streaming" + ) + } + } + + @Serializable + data class DocExample( + @Serializable(DocJsonListSerializer::class) val data: String + ) + + object DocJsonListSerializer : + JsonTransformingSerializer<String>(serializer()) { + override fun transformDeserialize(element: JsonElement): JsonElement { + if (element !is JsonArray) return element + require(element.size == 1) { "Array size must be equal to 1 to unwrap it" } + return element.first() + } + } + + @Test + fun testDocumentationSample() = parametrizedTest { streaming -> + val correctExample = DocExample("str1") + assertEquals(correctExample, json.decodeFromString(DocExample.serializer(), """{"data":["str1"]}""", streaming)) + assertEquals(correctExample, json.decodeFromString(DocExample.serializer(), """{"data":"str1"}""", streaming)) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTransientTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTransientTest.kt new file mode 100644 index 00000000..ebe06313 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTransientTest.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +@file:Suppress("EqualsOrHashCode") + +package kotlinx.serialization.json + +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient +import kotlinx.serialization.json.internal.* +import kotlinx.serialization.test.assertFailsWithSerial +import kotlin.test.* + +class JsonTransientTest : JsonTestBase() { + + @Serializable + class Data(val a: Int = 0, @Transient var b: Int = 42, val e: Boolean = false) { + var c = "Hello" + val d: String + get() = "hello" + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as Data + + if (a != other.a) return false + if (b != other.b) return false + if (c != other.c) return false + if (d != other.d) return false + + return true + } + + override fun toString(): String { + return "Data(a=$a, b=$b, e=$e, c='$c', d='$d')" + } + } + + @Test + fun testAll() = parametrizedTest { jsonTestingMode -> + assertEquals("""{"a":0,"e":false,"c":"Hello"}""", + default.encodeToString(Data.serializer(), Data(), jsonTestingMode)) + } + + @Test + fun testMissingOptionals() = parametrizedTest { jsonTestingMode -> + assertEquals(default.decodeFromString(Data.serializer(), """{"a":0,"c":"Hello"}""", jsonTestingMode), Data()) + assertEquals(default.decodeFromString(Data.serializer(), """{"a":0}""", jsonTestingMode), Data()) + } + + @Test + fun testThrowTransient() = parametrizedTest { jsonTestingMode -> + assertFailsWithSerial("JsonDecodingException") { + default.decodeFromString(Data.serializer(), """{"a":0,"b":100500,"c":"Hello"}""", jsonTestingMode) + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTreeAndMapperTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTreeAndMapperTest.kt new file mode 100644 index 00000000..336f630e --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTreeAndMapperTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlin.test.* + +class JsonTreeAndMapperTest { + private val decoderData = """{"id":0,"payload":{"from":42,"to":43,"msg":"Hello world"},"timestamp":1000}""" + private val decoderError = """{"id":1,"payload":{"error":"Connection timed out"},"timestamp":1001}""" + + @Serializable + data class Payload(val from: Long, val to: Long, val msg: String) + + sealed class Either { + data class Left(val errorMsg: String) : Either() + data class Right(val data: Payload) : Either() + } + + object EitherSerializer : KSerializer<Either> { + override val descriptor: SerialDescriptor = buildSerialDescriptor("Either", PolymorphicKind.SEALED) { + val leftDescriptor = buildClassSerialDescriptor("Either.Left") { + element<String>("errorMsg") + } + val rightDescriptor = buildClassSerialDescriptor("Either.Right") { + element("data", Payload.serializer().descriptor) + } + element("left", leftDescriptor) + element("right", rightDescriptor) + } + + override fun deserialize(decoder: Decoder): Either { + val input = decoder as? JsonDecoder ?: throw SerializationException("This class can be loaded only by Json") + val tree = input.decodeJsonElement() as? JsonObject + ?: throw SerializationException("Expected JsonObject") + if ("error" in tree) return Either.Left(tree.getValue("error").jsonPrimitive.content) + + return Either.Right(input.json.decodeFromJsonElement(Payload.serializer(), tree)) + } + + override fun serialize(encoder: Encoder, value: Either) { + val output = encoder as? JsonEncoder ?: throw SerializationException("This class can be saved only by Json") + val tree = when (value) { + is Either.Left -> JsonObject(mapOf("error" to JsonPrimitive(value.errorMsg))) + is Either.Right -> output.json.encodeToJsonElement(Payload.serializer(), value.data) + } + + output.encodeJsonElement(tree) + } + } + + @Serializable + data class Event( + val id: Int, + @Serializable(with = EitherSerializer::class) val payload: Either, + val timestamp: Long + ) + + @Test + fun testParseData() { + val ev = Json.decodeFromString(Event.serializer(), decoderData) + with(ev) { + assertEquals(0, id) + assertEquals(Either.Right(Payload(42, 43, "Hello world")), payload) + assertEquals(1000, timestamp) + } + } + + @Test + fun testParseError() { + val ev = Json.decodeFromString(Event.serializer(), decoderError) + with(ev) { + assertEquals(1, id) + assertEquals(Either.Left("Connection timed out"), payload) + assertEquals(1001, timestamp) + } + } + + @Test + fun testWriteData() { + val encoderData = Event(0, Either.Right(Payload(42, 43, "Hello world")), 1000) + val ev = Json.encodeToString(Event.serializer(), encoderData) + assertEquals(decoderData, ev) + } + + @Test + fun testWriteError() { + val encoderError = Event(1, Either.Left("Connection timed out"), 1001) + val ev = Json.encodeToString(Event.serializer(), encoderError) + assertEquals(decoderError, ev) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTreeImplicitNullsTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTreeImplicitNullsTest.kt new file mode 100644 index 00000000..995459e3 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTreeImplicitNullsTest.kt @@ -0,0 +1,14 @@ +package kotlinx.serialization.json + +import kotlinx.serialization.KSerializer + +class JsonTreeImplicitNullsTest: AbstractJsonImplicitNullsTest() { + override fun <T> Json.encode(value: T, serializer: KSerializer<T>): String { + return encodeToJsonElement(serializer, value).toString() + } + + override fun <T> Json.decode(json: String, serializer: KSerializer<T>): T { + val jsonElement = parseToJsonElement(json) + return decodeFromJsonElement(serializer, jsonElement) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTreeTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTreeTest.kt new file mode 100644 index 00000000..6e600386 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTreeTest.kt @@ -0,0 +1,151 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class JsonTreeTest : JsonTestBase() { + @Serializable + data class Data(val a: Int) + + @Serializable + data class DataWrapper(val s: String, val d: Data?) + + @Serializable + data class DataWrapperOptional(val s: String, val d: Data? = null) + + @Serializable + data class IntList(val l: List<Int>) + + @Serializable + data class DataList(val l: List<Data>) + + @Serializable + data class ListOfLists(val l: List<List<Data>>) + + @Serializable + data class MapWrapper(val m: Map<String, Int>) + + @Serializable + data class ComplexMapWrapper(val m: Map<String, Data>) + + @Serializable + data class AllTypes( + val b: Byte, + val s: Short, + val i: Int, + val f: Float, + val d: Double, + val c: Char, + val B: Boolean, + val S: String + ) + + private val json = Json + private fun prepare(input: String): JsonElement = lenient.parseToJsonElement(input) + + @Test + fun testReadTreeSimple() { + val tree = prepare("{a: 42}") + val parsed = lenient.decodeFromJsonElement(Data.serializer(), tree) + assertEquals(Data(42), parsed) + } + + @Test + fun testReadTreeNested() { + val tree = prepare("""{s:"foo", d:{a:42}}""") + val parsed = lenient.decodeFromJsonElement(DataWrapper.serializer(), tree) + val expected = DataWrapper("foo", Data(42)) + assertEquals(expected, parsed) + assertEquals(3, parsed.s.length) + } + + @Test + fun testReadTreeAllTypes() { + val tree = prepare("""{ b: 1, s: 2, i: 3, f: 1.0, d: 42.0, c: "a", B: true, S: "str"}""") + val kotlinObj = AllTypes(1, 2, 3, 1.0f, 42.0, 'a', true, "str") + + assertEquals(kotlinObj, json.decodeFromJsonElement(AllTypes.serializer(), tree)) + } + + @Test + fun testReadTreeNullable() { + val tree1 = prepare("""{s:"foo", d: null}""") + val tree2 = prepare("""{s:"foo"}""") + + assertEquals(DataWrapper("foo", null), lenient.decodeFromJsonElement(DataWrapper.serializer(), tree1)) + assertFailsWithMissingField { lenient.decodeFromJsonElement(DataWrapper.serializer(), tree2) } + } + + @Test + fun testReadTreeOptional() { + val tree1 = prepare("""{s:"foo", d: null}""") + val tree2 = prepare("""{s:"foo"}""") + + assertEquals(DataWrapperOptional("foo", null), json.decodeFromJsonElement(DataWrapperOptional.serializer(), tree1)) + assertEquals(DataWrapperOptional("foo", null), json.decodeFromJsonElement(DataWrapperOptional.serializer(), tree2)) + } + + @Test + fun testReadTreeList() { + val tree1 = prepare("""{l:[1,2]}""") + val tree2 = prepare("""{l:[{a:42},{a:43}]}""") + val tree3 = prepare("""{l:[[],[{a:42}]]}""") + + assertEquals(IntList(listOf(1, 2)), lenient.decodeFromJsonElement(IntList.serializer(), tree1)) + assertEquals(DataList(listOf(Data(42), Data(43))), lenient.decodeFromJsonElement(DataList.serializer(), tree2)) + assertEquals(ListOfLists(listOf(listOf(), listOf(Data(42)))), json.decodeFromJsonElement(ListOfLists.serializer(), tree3)) + } + + @Test + fun testReadTreeMap() { + val dyn = prepare("{m : {\"a\": 1, \"b\" : 2}}") + val m = MapWrapper(mapOf("a" to 1, "b" to 2)) + assertEquals(m, lenient.decodeFromJsonElement(MapWrapper.serializer(), dyn)) + } + + @Test + fun testReadTreeComplexMap() { + val dyn = prepare("{m : {1: {a: 42}, 2: {a: 43}}}") + val m = ComplexMapWrapper(mapOf("1" to Data(42), "2" to Data(43))) + assertEquals(m, lenient.decodeFromJsonElement(ComplexMapWrapper.serializer(), dyn)) + } + + private inline fun <reified T: Any> writeAndTest(obj: T, serial: KSerializer<T>, printDiagnostics: Boolean = false): Pair<JsonElement, T> { + val tree = lenient.encodeToJsonElement(serial, obj) + val str = tree.toString() + if (printDiagnostics) println(str) + val restored = lenient.decodeFromJsonElement(serial, lenient.parseToJsonElement(str)) + assertEquals(obj, restored) + return tree to restored + } + + @Test + fun testSaveSimpleNestedTree() { + writeAndTest(DataWrapper("foo", Data(42)), DataWrapper.serializer()) + } + + @Test + fun testSaveComplexMapTree() { + writeAndTest(ComplexMapWrapper(mapOf("foo" to Data(42), "bar" to Data(43))), ComplexMapWrapper.serializer()) + } + + @Test + fun testSaveNestedLists() { + writeAndTest(ListOfLists(listOf(listOf(), listOf(Data(1), Data(2)))), ListOfLists.serializer()) + } + + @Test + fun testSaveOptional() { + writeAndTest(DataWrapperOptional("foo", null), DataWrapperOptional.serializer()) + } + + @Test + fun testSaveAllTypes() { + writeAndTest(AllTypes(1, -2, 100500, 0.0f, 2048.2, 'a', true, "foobar"), AllTypes.serializer()) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonUnicodeTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonUnicodeTest.kt new file mode 100644 index 00000000..1f6f814f --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonUnicodeTest.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlinx.serialization.test.* +import kotlin.random.* +import kotlin.test.* + +class JsonUnicodeTest : JsonTestBase() { + + @Serializable + data class UnicodeKeys( + @SerialName("\uD83E\uDD14") val thinking: String, + @SerialName("🤔?") val thinking2: String, + @SerialName("\uD83E\uDD15") val bandage: String, + @SerialName("\"") val escaped: String + ) + + @Test + fun testUnicodeKeys() { + val data = UnicodeKeys("1", "2", "3", "4") + val s = """{"\uD83E\uDD14":"1","\uD83E\uDD14?":"2","\uD83E\uDD15":"3","\"":"4"}""" + assertEquals(data, Json.decodeFromString(s)) + } + + @Test + fun testUnicodeValues() { + val data = UnicodeKeys( + "\uD83E\uDD14", "\" \uD83E\uDD14", "\uD83E\uDD14", + "slow-path-in-\"-the-middle\"" + ) + assertEquals(data, Json.decodeFromString(Json.encodeToString(data))) + } + + @Serializable + data class Wrapper(val s: String) + + @Test + fun testLongEscapeSequence() { + assertSerializedAndRestored(Wrapper("\"".repeat(100)), Wrapper.serializer()) + // #1456 + assertSerializedAndRestored( + Wrapper("{\"status\":123,\"message\":\"content\",\"path\":\"/here/beeeeeeeeeeee/dragoons/d63574f-705c-49dd-a6bc-c8d1e524eefd/\"}"), + Wrapper.serializer() + ) + // #1460 + assertSerializedAndRestored( + """{"avatar_url":"https://cdn.discordapp.com/avatars/384333349063491584/8adca1bddf8c5c46c7deed3edbd80d60.png","embeds":[{"color":1741274,"author":{"icon_url":"https://pbs.twimg.com/profile_images/1381321181719109633/4bpPMaer_normal.jpg","name":"Merlijn replied:","url":"https://twitter.com/@PixelHamster/status/1390719238155952129"},"description":"[@shroomizu](https://twitter.com/shroomizu) time for a pro controller","type":"rich","timestamp":"2021-05-07T17:24:39Z"}],"username":"Merijn"}""", + String.serializer() + ) + + assertSerializedAndRestored( + Wrapper("null抢\u000e鋽윝䑜厼\uF70A紲ᢨ䣠null⛾䉻嘖緝ᯧnull쎶\u0005null\u0013\uECC9null藜null㴦铰\\bnull\\f똆\u0010蕧⛺null\u0014毣檚牅䈏nullnullnullnullnullᅤ\uF7E1쒪null퓈nullnullnullnullnull?null\uF7EA釸팔null蒒\uF840\u0014\u001c\u000f彏\u000fnull㩻ㅃ\u0005\u001fnull풸\u0011\u0FBDnullnullnull\u0006ᡗ撺\u000enullnullnullnull\u2DBF\uED3C굃nullۃnull䨻醙䗰?\uE5EF\uE656null\uE819?\u0017null㈰nullࣝȰ\uF485\u0017null浣䃛搞nullnull?ⴃ뎝null튫nullהꀠnullﲪnull\u000b\u000enull\u001d猶ᄐnullnullnull鎡null⤏null睏뫌ꛖ\\\"nullnull芘䭠\u0006\u0005ቲ慔\uE8A8ٱ琹?nullnullnull\\t\u0EFA\uEDC9괙?\u0007⠍\u001c\uF5D9랴nullnull\\t묽null甄\uED64䥀null\u0007婍\\b\u0002\\\\null臍nullnullnullnull巓\u000f剛\u0004滴\u0010nullnullnullnullnull烌\u0006nullnull풜null\uEF0F\u001b\u0005null鱽劝nullnull닙nullnullnullnullnullnull⧝\u000bnullnull↥\\t杽鵀nullnull嘡nullnullꑁ\u0007뀨nullnullꑜ䇓亶v鶣\uE4C8\\bnull퇾nullnullޕnull⻗፷null\u001dnullnullnullnull\\b⢅놫賗ᯱ\uF44A\u0016\uF8B7扈\\fnullnull妯nullnullพnull쨆null⤥슅\u0006㥹\u0016null滀null㼻\u0007null搽뿙툛單賎猘nullnull\u0005춎null兀nullnull诃䅤nullnull鵺쒢ꦀnull諟\u001c훕nullጐnull\u000bƒ볤ː老\u001enull抅ǩౄ켎null吿null\uFDCA\u000b૧奵nullﶼ㣘null\u001c\uF085nullꉫ腈null뛅⚼갩nullnullnullꕂ䦾돶\u001e\u2EFF\u0010\uE67Cnull\u0000\u0005null\\\\null䣉\u001b\u0017\u0000\u001f萵\u0007Ꞃ驿\u0005ḁnull臅\u0007nullnullnull\u0003킍Շ矙\uEB60複null\uF5E1null햗灶null\\r\u0002\uE76Fꢬnullϗ뺤⫷nullnull⾿null苵nullnull퉰\u000f\\b摮null\u0019利\\b䷈ꡎ蒏null븘\uF07E\u0015\u0017nullnull䔐\u000f\u001a\\t꣪nullླྀ?\\bnullnullnull묣null鎱熅null\\n鳜鱽黦ꘜ뱈\u0EDAnullnullnullnullnullnull뫾바\u0007꩕푊\u001cnullnull套\u0017null\u0004샂null饘null\uF2EB툛nullnullnull옉\uE2B7null\uED6Fnull\u0004罟null\u0011˟\\f荲null팩\uFD4Anull⥴\uEA9Enullnull⡎搻狯nullnullӋnull翅ભ\u0007䏟null\uF043\u0011null跩nullnull倿\u09BB賘null\uE514⾙nullꭜѲం\\\"⎾null땈\uF36E\u0007\uE6D9null婣㭁null\uE570nullnull傢nullnull\uF6DB慞嘼null\u187B㺯⍹ᘃ㝛年쇡null讬枏䠜昅᾽null檳\u0010\u0007\\t֊壑ﵐ詇nullnull\u001e\uF3D7\\r妗뙇null퐁null\u0003㖣뢠ᮉ䏁null蘜\u000e\u0006null졎nullnull?庪null䞺黩null\u000e뎤null\u0013null\u001d윸\u20F6ᆵ\uF57B\\\"猬null\u001bnullnullnullnull抙\uEAC3ꢨ\u001b\uF0C6\u0002\uF41Enull\u0014\uF3C5訇깤吀匭?퐺\u000eꩦnull\u0004\u0013恧null眜null\u0015飥nullసnullⅆnullnull\u0002nullnull\uE442\uF2A0闛null渜null㊄\u0001≧긷null螥\\\\nullቹčnull\u0018null箱nullnull端\uF7B5⋒䝂\u001c饆抋\u001eᐳ\\r공nullnull\u1739null쨒ꭇnull\\f\u0003\u0018null햖㈙\u001enullꃿnullꃦ\u0000null᧡뚦\uAACEnullnullnull\u0019null\u061C㞮췦\u000e൝null\u0014\u0015null揰null\u0018null禟斛♷\uEAB3nullnull\uE82D罁ꆟnull\\fnullnullլ癱㗋䢵?\u0015\u0005㪙췗null\u0006帡\u0013倫ﴚnull⫣\u0000鲉null\u001dnull\u0010쓸릌null\u0005⨓null疰\u000b偒nullnull\uEAA4ꕸnull塯䩡쀍null?nullnullnull●null\u0000\\f\uE3EB悔榴촓䉁椙null䓖null\u0005null鉌nullnullnull⦙null\uEA21null\u0011\u001enull\u001enull偲\u001cnullnull좩nullᚫ꛱null원꒒null䨉㚁⋎null\u0002镅\uEF30댵\\fnull촴뺴됶肎null鯑詺\uF618nullnull녽null眴nullnull郱ᘘ斮궡nullnull뛋⋎榩?딡nullnull?\u0014蘉\uF2E1null\\bnullnull추null\u0005㉁ꤙꇈ姱null㪹\u0002㍈nullnullnull\u001f㇇\u0017蒷墛nullɩ\\\"null\u000f⬆襤nullnull┭ഀ溜ࢳ❧亠null컈\u0019\\tnullnullﳎnull\u0007null\\t⧘\u0014nullﲘnull끣nullᯜ㧭푬쓉null\u001anull〪䣩䃂ﺤ찅null恗⏗䅳null⭮nullnull\u0016nullnullnull\uE825郞㬃嶘null\\n꼻\uF08Enull\uE4D7⒙䑮null\uE0E2nullnull䢱null\u0003\uEBFB苚釳Ǧ\u0012\u000b\u0011nullnull\\bnullഏ?\u000fﵕ鋨짰\u0001null憞\u001b๘null祆\u001cnull॒꺰\u0010ѻnull?\u000b˻갇null㍿\uE63C㖝\u0016匃\\n\u0001null\\rnullnullᅋnull\uE365↾抬ꠁnull㾁ᕚ헕챱\u0002\uEDB5\u0010null凵\u1ADA༽Ꙋ긚nullᵪnull\\bnullᄈ퍶null얄팼ަᔄ\uF120nullᬍ\\r⍭null퀉蟸焋\\fnull⮏nullnull?\u0012\u0017갞?Ꮵnull┌\\\"\uE803nullnullnullɂ财⎱null엏null娮nullnullnull꛰null吣\u0019ヷ맾ᣝ\uE766ﰟnullnullnullnull祐null\u0000null\u0C84뽉툑null 폍윲\u001d愼ᱳnullꁮnull㕃null가null㗱nullצnull\u0001null춊\u001fힾ\u001a\u0016┒옎璻电絒\u0015\u001bၑnull\u0006\u0006\u0002㳟nullnullԈnull\u1F5Anullnullnull\u000fnullnullnullЧ曗კ\uF5A8null錦Ӣnull\u000b"), + Wrapper.serializer() + ) + } + + @Test + fun testRandomEscapeSequences() = noJs { // Too slow on JS + repeat(10_000) { + val s = generateRandomUnicodeString(Random.nextInt(1, 2047)) + try { + assertSerializedAndRestored(s, String.serializer()) + } catch (e: Throwable) { + // Not assertion error to preserve cause + throw IllegalStateException("Unexpectedly failed test, cause string: $s", e) + } + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonUnionEnumTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonUnionEnumTest.kt new file mode 100644 index 00000000..76634bbc --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonUnionEnumTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlin.test.Test +import kotlin.test.assertEquals + +class JsonUnionEnumTest : JsonTestBase() { + + enum class SomeEnum { ALPHA, BETA, GAMMA } + + @Serializable + data class WithUnions(val s: String, + val e: SomeEnum = SomeEnum.ALPHA, + val i: Int = 42) + + @Test + fun testEnum() = parametrizedTest { jsonTestingMode -> + val data = WithUnions("foo", SomeEnum.BETA) + val json = default.encodeToString(WithUnions.serializer(), data, jsonTestingMode) + val restored = default.decodeFromString(WithUnions.serializer(), json, jsonTestingMode) + assertEquals(data, restored) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonUpdateModeTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonUpdateModeTest.kt new file mode 100644 index 00000000..eccef39d --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonUpdateModeTest.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlin.test.* + +class JsonOverwriteTest : JsonTestBase() { + @Serializable + data class Updatable1(val l: List<Int>) + + @Serializable + data class Data(val a: Int) + + @Serializable + data class Updatable2(val l: List<Data>) + + @Serializable + data class NullableInnerIntList(val data: List<Int?>) + + @Serializable + data class NullableUpdatable(val data: List<Data>?) + + @Test + fun testCanUpdatePrimitiveList() = parametrizedTest { jsonTestingMode -> + val parsed = + lenient.decodeFromString<Updatable1>(Updatable1.serializer(), """{"l":[1,2],"f":"foo","l":[3,4]}""", jsonTestingMode) + assertEquals(Updatable1(listOf(3, 4)), parsed) + } + + @Test + fun testCanUpdateObjectList() = parametrizedTest { jsonTestingMode -> + val parsed = lenient.decodeFromString<Updatable2>( + Updatable2.serializer(), + """{"f":"bar","l":[{"a":42}],"l":[{"a":43}]}""", + jsonTestingMode + ) + assertEquals(Updatable2(listOf(Data(43))), parsed) + } + + @Test + fun testCanUpdateNullableValuesInside() = parametrizedTest { jsonTestingMode -> + val a1 = default.decodeFromString(NullableInnerIntList.serializer(), """{"data":[null],"data":[1]}""", jsonTestingMode) + assertEquals(NullableInnerIntList(listOf(1)), a1) + val a2 = default.decodeFromString(NullableInnerIntList.serializer(), """{"data":[42],"data":[null]}""", jsonTestingMode) + assertEquals(NullableInnerIntList(listOf(null)), a2) + val a3 = default.decodeFromString(NullableInnerIntList.serializer(), """{"data":[31],"data":[1]}""", jsonTestingMode) + assertEquals(NullableInnerIntList(listOf(1)), a3) + } + + @Test + fun testCanUpdateNullableValues() = parametrizedTest { jsonTestingMode -> + val a1 = lenient.decodeFromString(NullableUpdatable.serializer(), """{"data":null,"data":[{"a":42}]}""", jsonTestingMode) + assertEquals(NullableUpdatable(listOf(Data(42))), a1) + val a2 = lenient.decodeFromString(NullableUpdatable.serializer(), """{"data":[{a:42}],"data":null}""", jsonTestingMode) + assertEquals(NullableUpdatable(null), a2) + val a3 = lenient.decodeFromString(NullableUpdatable.serializer(), """{"data":[{a:42}],"data":[{"a":43}]}""", jsonTestingMode) + assertEquals(NullableUpdatable(listOf(Data(43))), a3) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/LenientTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/LenientTest.kt new file mode 100644 index 00000000..d24c7d7c --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/LenientTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlinx.serialization.json.internal.* +import kotlinx.serialization.test.assertFailsWithSerial +import kotlin.test.* + +class LenientTest : JsonTestBase() { + + @Serializable + data class Holder(val i: Int, val l: Long, val b: Boolean, val s: String) + val value = Holder(1, 2, true, "string") + + @Serializable + data class ListHolder(val l: List<String>) + private val listValue = ListHolder(listOf("1", "2", "ss")) + + @Test + fun testQuotedInt() = parametrizedTest { + val json = """{"i":"1", "l":2, "b":true, "s":"string"}""" + assertEquals(value, default.decodeFromString(Holder.serializer(), json, it)) + assertEquals(value, lenient.decodeFromString(Holder.serializer(), json, it)) + } + + @Test + fun testQuotedLong() = parametrizedTest { + val json = """{"i":1, "l":"2", "b":true, "s":"string"}""" + assertEquals(value, default.decodeFromString(Holder.serializer(), json, it)) + assertEquals(value, lenient.decodeFromString(Holder.serializer(), json, it)) + } + + @Test + fun testQuotedBoolean() = parametrizedTest { + val json = """{"i":1, "l":2, "b":"true", "s":"string"}""" + assertEquals(value, default.decodeFromString(Holder.serializer(), json, it)) + assertEquals(value, lenient.decodeFromString(Holder.serializer(), json, it)) + } + + @Test + fun testUnquotedStringValue() = parametrizedTest { + val json = """{"i":1, "l":2, "b":true, "s":string}""" + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString(Holder.serializer(), json, it) } + assertEquals(value, lenient.decodeFromString(Holder.serializer(), json, it)) + } + + @Test + fun testUnquotedKey() = parametrizedTest { + val json = """{"i":1, "l":2, b:true, "s":"string"}""" + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString(Holder.serializer(), json, it) } + assertEquals(value, lenient.decodeFromString(Holder.serializer(), json, it)) + } + + @Test + fun testUnquotedStringInArray() = parametrizedTest { + val json = """{"l":[1, 2, ss]}""" + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString(ListHolder.serializer(), json, it) } + assertEquals(listValue, lenient.decodeFromString(ListHolder.serializer(), json, it)) + } + + @Serializable + data class StringWrapper(val s: String) + + @Test + fun testNullsProhibited() = parametrizedTest { + assertEquals(StringWrapper("nul"), lenient.decodeFromString("""{"s":nul}""", it)) + assertEquals(StringWrapper("null1"), lenient.decodeFromString("""{"s":null1}""", it)) + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString<StringWrapper>("""{"s":null}""", it) } + } + + @Serializable + data class NullableString(val s: String?) + + @Test + fun testNullsAllowed() = parametrizedTest { + assertEquals(NullableString("nul"), lenient.decodeFromString("""{"s":nul}""", it)) + assertEquals(NullableString("null1"), lenient.decodeFromString("""{"s":null1}""", it)) + assertEquals(NullableString(null), lenient.decodeFromString("""{"s":null}""", it)) + assertEquals(NullableString("null"), lenient.decodeFromString("""{"s":"null"}""", it)) + assertEquals(NullableString("null"), lenient.decodeFromString("""{"s":"null" }""", it)) + assertEquals(NullableString("null "), lenient.decodeFromString("""{"s":"null " }""", it)) + } + + @Test + fun testTopLevelNulls() = parametrizedTest { + assertEquals("nul", lenient.decodeFromString("""nul""", it)) + assertEquals("null1", lenient.decodeFromString("""null1""", it)) + assertEquals(null, lenient.decodeFromString(String.serializer().nullable, """null""", it)) + assertEquals("null", lenient.decodeFromString(""""null"""", it)) + assertEquals("null ", lenient.decodeFromString(""""null """", it)) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/MapLikeSerializerTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/MapLikeSerializerTest.kt new file mode 100644 index 00000000..bd8f1045 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/MapLikeSerializerTest.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlin.test.* + +class MapLikeSerializerTest : JsonTestBase() { + + @Serializable + data class StringPair(val a: String, val b: String) + + object StringPairSerializer : KSerializer<StringPair> { + + override val descriptor: SerialDescriptor = buildSerialDescriptor("package.StringPair", StructureKind.MAP) { + element<String>("a") + element<String>("b") + } + + override fun serialize(encoder: Encoder, value: StringPair) { + val structuredEncoder = encoder.beginStructure(descriptor) + structuredEncoder.encodeSerializableElement(descriptor, 0, String.serializer(), value.a) + structuredEncoder.encodeSerializableElement(descriptor, 1, String.serializer(), value.b) + structuredEncoder.endStructure(descriptor) + } + + override fun deserialize(decoder: Decoder): StringPair { + val composite = decoder.beginStructure(descriptor) + if (composite.decodeSequentially()) { + val key = composite.decodeSerializableElement(descriptor, 0, String.serializer()) + val value = composite.decodeSerializableElement(descriptor, 1, String.serializer()) + return StringPair(key, value) + } + + var key: String? = null + var value: String? = null + mainLoop@ while (true) { + when (val idx = composite.decodeElementIndex(descriptor)) { + CompositeDecoder.DECODE_DONE -> { + break@mainLoop + } + 0 -> { + key = composite.decodeSerializableElement(descriptor, 0, String.serializer()) + } + 1 -> { + value = composite.decodeSerializableElement(descriptor, 1, String.serializer()) + } + else -> throw SerializationException("Invalid index: $idx") + } + } + composite.endStructure(descriptor) + if (key == null) throw SerializationException("Element 'a' is missing") + if (value == null) throw SerializationException("Element 'b' is missing") + @Suppress("UNCHECKED_CAST") + return StringPair(key, value) + } + } + + @Test + fun testStringPair() = assertJsonFormAndRestored(StringPairSerializer, StringPair("a", "b"), """{"a":"b"}""") +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/SpecialFloatingPointValuesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/SpecialFloatingPointValuesTest.kt new file mode 100644 index 00000000..fad07e62 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/SpecialFloatingPointValuesTest.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.json.internal.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class SpecialFloatingPointValuesTest : JsonTestBase() { + + @Serializable + data class Box(val d: Double, val f: Float) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + other as Box + if (d != other.d && !(d.isNaN() && other.d.isNaN())) return false + if (f != other.f && !(f.isNaN() && other.f.isNaN())) return false + return true + } + + override fun hashCode(): Int { + var result = d.hashCode() + result = 31 * result + f.hashCode() + return result + } + } + + val json = Json { allowSpecialFloatingPointValues = true } + + @Test + fun testNans() = parametrizedTest { + test(Box(Double.NaN, Float.NaN), """{"d":NaN,"f":NaN}""", it) + noJs { // Number formatting + test(Box(0.0, Float.NaN), """{"d":0.0,"f":NaN}""", it) + test(Box(Double.NaN, 0.0f), """{"d":NaN,"f":0.0}""", it) + } + } + + @Test + fun testInfinities() = parametrizedTest { + test(Box(Double.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY), """{"d":-Infinity,"f":Infinity}""", it) + test(Box(Double.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY), """{"d":Infinity,"f":-Infinity}""", it) + } + + private fun test(box: Box, expected: String, jsonTestingMode: JsonTestingMode) { + assertFailsWithSerialMessage("JsonEncodingException", "Unexpected special floating-point value") { default.encodeToString(Box.serializer(), box, jsonTestingMode) } + assertEquals(expected, json.encodeToString(Box.serializer(), box, jsonTestingMode)) + assertEquals(box, json.decodeFromString(Box.serializer(), expected, jsonTestingMode)) + assertFailsWithSerialMessage("JsonDecodingException", "Unexpected special floating-point value") { default.decodeFromString(Box.serializer(), expected, jsonTestingMode) } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/TrailingCommaTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/TrailingCommaTest.kt new file mode 100644 index 00000000..0916de57 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/TrailingCommaTest.kt @@ -0,0 +1,128 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class TrailingCommaTest : JsonTestBase() { + val tj = Json { allowTrailingComma = true } + + @Serializable + data class Optional(val data: String = "") + + @Serializable + data class MultipleFields(val a: String, val b: String, val c: String) + + private val multipleFields = MultipleFields("1", "2", "3") + + @Serializable + data class WithMap(val m: Map<String, String>) + + private val withMap = WithMap(mapOf("a" to "1", "b" to "2", "c" to "3")) + + @Serializable + data class WithList(val l: List<Int>) + + private val withList = WithList(listOf(1, 2, 3)) + + @Test + fun basic() = parametrizedTest { mode -> + val sd = """{"data":"str",}""" + assertEquals(Optional("str"), tj.decodeFromString<Optional>(sd, mode)) + } + + @Test + fun trailingCommaNotAllowedByDefaultForObjects() = parametrizedTest { mode -> + val sd = """{"data":"str",}""" + checkSerializationException({ + default.decodeFromString<Optional>(sd, mode) + }, { message -> + assertContains( + message, + """Unexpected JSON token at offset 13: Trailing comma before the end of JSON object""" + ) + }) + } + + @Test + fun trailingCommaNotAllowedByDefaultForLists() = parametrizedTest { mode -> + val sd = """{"l":[1,]}""" + checkSerializationException({ + default.decodeFromString<WithList>(sd, mode) + }, { message -> + assertContains( + message, + """Unexpected JSON token at offset 7: Trailing comma before the end of JSON array""" + ) + }) + } + + @Test + fun trailingCommaNotAllowedByDefaultForMaps() = parametrizedTest { mode -> + val sd = """{"m":{"a": "b",}}""" + checkSerializationException({ + default.decodeFromString<WithMap>(sd, mode) + }, { message -> + assertContains( + message, + """Unexpected JSON token at offset 14: Trailing comma before the end of JSON object""" + ) + }) + } + + @Test + fun emptyObjectNotAllowed() = parametrizedTest { mode -> + assertFailsWithMessage<SerializationException>("Unexpected leading comma") { + tj.decodeFromString<Optional>("""{,}""", mode) + } + } + + @Test + fun emptyListNotAllowed() = parametrizedTest { mode -> + assertFailsWithMessage<SerializationException>("Unexpected leading comma") { + tj.decodeFromString<WithList>("""{"l":[,]}""", mode) + } + } + + @Test + fun emptyMapNotAllowed() = parametrizedTest { mode -> + assertFailsWithMessage<SerializationException>("Unexpected leading comma") { + tj.decodeFromString<WithMap>("""{"m":{,}}""", mode) + } + } + + @Test + fun testMultipleFields() = parametrizedTest { mode -> + val input = """{"a":"1","b":"2","c":"3", }""" + assertEquals(multipleFields, tj.decodeFromString(input, mode)) + } + + @Test + fun testWithMap() = parametrizedTest { mode -> + val input = """{"m":{"a":"1","b":"2","c":"3", }}""" + + assertEquals(withMap, tj.decodeFromString(input, mode)) + } + + @Test + fun testWithList() = parametrizedTest { mode -> + val input = """{"l":[1, 2, 3, ]}""" + assertEquals(withList, tj.decodeFromString(input, mode)) + } + + @Serializable + data class Mixed(val mf: MultipleFields, val wm: WithMap, val wl: WithList) + + @Test + fun testMixed() = parametrizedTest { mode -> + //language=JSON5 + val input = """{"mf":{"a":"1","b":"2","c":"3",}, + "wm":{"m":{"a":"1","b":"2","c":"3",},}, + "wl":{"l":[1, 2, 3,],},}""" + assertEquals(Mixed(multipleFields, withMap, withList), tj.decodeFromString(input, mode)) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonClassDiscriminatorModeBaseTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonClassDiscriminatorModeBaseTest.kt new file mode 100644 index 00000000..8fcd5499 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonClassDiscriminatorModeBaseTest.kt @@ -0,0 +1,153 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.polymorphic + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlin.test.* + +abstract class JsonClassDiscriminatorModeBaseTest( + val discriminator: ClassDiscriminatorMode, + val deserializeBack: Boolean = true +) : JsonTestBase() { + + @Serializable + sealed class SealedBase + + @Serializable + @SerialName("container") + data class SealedContainer(val i: Inner): SealedBase() + + @Serializable + @SerialName("inner") + data class Inner(val x: String, val e: SampleEnum = SampleEnum.OptionB) + + @Serializable + @SerialName("outer") + data class Outer(val inn: Inner, val lst: List<Inner>, val lss: List<String>) + + data class ContextualType(val text: String) + + object CtxSerializer : KSerializer<ContextualType> { + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("CtxSerializer") { + element("a", String.serializer().descriptor) + element("b", String.serializer().descriptor) + } + + override fun serialize(encoder: Encoder, value: ContextualType) { + encoder.encodeStructure(descriptor) { + encodeStringElement(descriptor, 0, value.text.substringBefore("#")) + encodeStringElement(descriptor, 1, value.text.substringAfter("#")) + } + } + + override fun deserialize(decoder: Decoder): ContextualType { + lateinit var a: String + lateinit var b: String + decoder.decodeStructure(descriptor) { + while (true) { + when (decodeElementIndex(descriptor)) { + 0 -> a = decodeStringElement(descriptor, 0) + 1 -> b = decodeStringElement(descriptor, 1) + else -> break + } + } + } + return ContextualType("$a#$b") + } + } + + @Serializable + @SerialName("withContextual") + data class WithContextual(@Contextual val ctx: ContextualType, val i: Inner) + + val ctxModule = serializersModuleOf(CtxSerializer) + + val json = Json(default) { + ignoreUnknownKeys = true + serializersModule = polymorphicTestModule + ctxModule + encodeDefaults = true + classDiscriminatorMode = discriminator + } + + @Serializable + @SerialName("mixed") + data class MixedPolyAndRegular(val sb: SealedBase, val sc: SealedContainer, val i: Inner) + + private inline fun <reified T> doTest(expected: String, obj: T) { + parametrizedTest { mode -> + val serialized = json.encodeToString(serializer<T>(), obj, mode) + assertEquals(expected, serialized, "Failed with mode = $mode") + if (deserializeBack) { + val deserialized: T = json.decodeFromString(serializer(), serialized, mode) + assertEquals(obj, deserialized, "Failed with mode = $mode") + } + } + } + + fun testMixed(expected: String) { + val i = Inner("in", SampleEnum.OptionC) + val o = MixedPolyAndRegular(SealedContainer(i), SealedContainer(i), i) + doTest(expected, o) + } + + fun testIncludeNonPolymorphic(expected: String) { + val o = Outer(Inner("X"), listOf(Inner("a"), Inner("b")), listOf("foo")) + doTest(expected, o) + } + + fun testIncludePolymorphic(expected: String) { + val o = OuterNullableBox(OuterNullableImpl(InnerImpl(42), null), InnerImpl2(239)) + doTest(expected, o) + } + + fun testIncludeSealed(expected: String) { + val b = Box<SealedBase>(SealedContainer(Inner("x", SampleEnum.OptionC))) + doTest(expected, b) + } + + fun testContextual(expected: String) { + val c = WithContextual(ContextualType("c#d"), Inner("x")) + doTest(expected, c) + } + + @Serializable + @JsonClassDiscriminator("message_type") + sealed class Base + + @Serializable // Class discriminator is inherited from Base + sealed class ErrorClass : Base() + + @Serializable + @SerialName("ErrorClassImpl") + data class ErrorClassImpl(val msg: String) : ErrorClass() + + @Serializable + @SerialName("Cont") + data class Cont(val ec: ErrorClass, val eci: ErrorClassImpl) + + fun testCustomDiscriminator(expected: String) { + val c = Cont(ErrorClassImpl("a"), ErrorClassImpl("b")) + doTest(expected, c) + } + + fun testTopLevelPolyImpl(expectedOpen: String, expectedSealed: String) { + assertEquals(expectedOpen, json.encodeToString(InnerImpl(42))) + assertEquals(expectedSealed, json.encodeToString(SealedContainer(Inner("x")))) + } + + @Serializable + @SerialName("NullableMixed") + data class NullableMixed(val sb: SealedBase?, val sc: SealedContainer?) + + fun testNullable(expected: String) { + val nm = NullableMixed(null, null) + doTest(expected, nm) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonClassDiscriminatorModeTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonClassDiscriminatorModeTest.kt new file mode 100644 index 00000000..b2f47137 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonClassDiscriminatorModeTest.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.polymorphic + +import kotlinx.serialization.json.* +import kotlin.test.* + +class ClassDiscriminatorModeAllObjectsTest : + JsonClassDiscriminatorModeBaseTest(ClassDiscriminatorMode.ALL_JSON_OBJECTS) { + @Test + fun testIncludeNonPolymorphic() = testIncludeNonPolymorphic("""{"type":"outer","inn":{"type":"inner","x":"X","e":"OptionB"},"lst":[{"type":"inner","x":"a","e":"OptionB"},{"type":"inner","x":"b","e":"OptionB"}],"lss":["foo"]}""") + + @Test + fun testIncludePolymorphic() { + val s = """{"type":"kotlinx.serialization.json.polymorphic.OuterNullableBox","outerBase":{"type":"kotlinx.serialization.json.polymorphic.OuterNullableImpl","""+ + """"base":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":42,"str":"default","nullable":null},"base2":null},"innerBase":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl2","field":239}}""" + testIncludePolymorphic(s) + } + + @Test + fun testIncludeSealed() { + testIncludeSealed("""{"type":"kotlinx.serialization.Box","boxed":{"type":"container","i":{"type":"inner","x":"x","e":"OptionC"}}}""") + } + + @Test + fun testIncludeMixed() = testMixed("""{"type":"mixed","sb":{"type":"container","i":{"type":"inner","x":"in","e":"OptionC"}},"sc":{"type":"container","i":{"type":"inner","x":"in","e":"OptionC"}},"i":{"type":"inner","x":"in","e":"OptionC"}}""") + + @Test + fun testIncludeCtx() = + testContextual("""{"type":"withContextual","ctx":{"type":"CtxSerializer","a":"c","b":"d"},"i":{"type":"inner","x":"x","e":"OptionB"}}""") + + @Test + fun testIncludeCustomDiscriminator() = + testCustomDiscriminator("""{"type":"Cont","ec":{"message_type":"ErrorClassImpl","msg":"a"},"eci":{"message_type":"ErrorClassImpl","msg":"b"}}""") + + @Test + fun testTopLevelPolyImpl() = testTopLevelPolyImpl( + """{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":42,"str":"default","nullable":null}""", + """{"type":"container","i":{"type":"inner","x":"x","e":"OptionB"}}""" + ) + + @Test + fun testNullable() = testNullable("""{"type":"NullableMixed","sb":null,"sc":null}""") + +} + +class ClassDiscriminatorModeNoneTest : + JsonClassDiscriminatorModeBaseTest(ClassDiscriminatorMode.NONE, deserializeBack = false) { + @Test + fun testIncludeNonPolymorphic() = testIncludeNonPolymorphic("""{"inn":{"x":"X","e":"OptionB"},"lst":[{"x":"a","e":"OptionB"},{"x":"b","e":"OptionB"}],"lss":["foo"]}""") + + @Test + fun testIncludePolymorphic() { + val s = """{"outerBase":{"base":{"field":42,"str":"default","nullable":null},"base2":null},"innerBase":{"field":239}}""" + testIncludePolymorphic(s) + } + + @Test + fun testIncludeSealed() { + testIncludeSealed("""{"boxed":{"i":{"x":"x","e":"OptionC"}}}""") + } + + @Test + fun testIncludeMixed() = testMixed("""{"sb":{"i":{"x":"in","e":"OptionC"}},"sc":{"i":{"x":"in","e":"OptionC"}},"i":{"x":"in","e":"OptionC"}}""") + + @Test + fun testIncludeCtx() = + testContextual("""{"ctx":{"a":"c","b":"d"},"i":{"x":"x","e":"OptionB"}}""") + + @Test + fun testIncludeCustomDiscriminator() = testCustomDiscriminator("""{"ec":{"msg":"a"},"eci":{"msg":"b"}}""") + + @Test + fun testTopLevelPolyImpl() = testTopLevelPolyImpl( + """{"field":42,"str":"default","nullable":null}""", + """{"i":{"x":"x","e":"OptionB"}}""" + ) + + @Test + fun testNullable() = testNullable("""{"sb":null,"sc":null}""") +} + diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonContentPolymorphicSerializerTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonContentPolymorphicSerializerTest.kt new file mode 100644 index 00000000..d58e26b6 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonContentPolymorphicSerializerTest.kt @@ -0,0 +1,103 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.polymorphic + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlin.test.* + +class JsonContentPolymorphicSerializerTest : JsonTestBase() { + val json = Json + + @Serializable + sealed class Choices { + @Serializable + data class HasA(val a: String) : Choices() + + @Serializable + data class HasB(val b: Int) : Choices() + + @Serializable + data class HasC(val c: Boolean) : Choices() + } + + object ChoicesParametricSerializer : JsonContentPolymorphicSerializer<Choices>(Choices::class) { + override fun selectDeserializer(element: JsonElement): KSerializer<out Choices> { + val obj = element.jsonObject + return when { + "a" in obj -> Choices.HasA.serializer() + "b" in obj -> Choices.HasB.serializer() + "c" in obj -> Choices.HasC.serializer() + else -> throw SerializationException("Unknown choice") + } + } + } + + @Serializable + data class WithChoices(@Serializable(ChoicesParametricSerializer::class) val response: Choices) + + private val testDataInput = listOf( + """{"response":{"a":"string"}}""", + """{"response":{"b":42}}""", + """{"response":{"c":true}}""" + ) + + private val testDataOutput = listOf( + WithChoices(Choices.HasA("string")), + WithChoices(Choices.HasB(42)), + WithChoices(Choices.HasC(true)) + ) + + @Test + fun testParsesParametrically() = parametrizedTest { streaming -> + for (i in testDataInput.indices) { + assertEquals( + testDataOutput[i], + json.decodeFromString(WithChoices.serializer(), testDataInput[i], streaming), + "failed test on ${testDataInput[i]}, jsonTestingMode = $streaming" + ) + } + } + + @Test + fun testSerializesParametrically() = parametrizedTest { streaming -> + for (i in testDataOutput.indices) { + assertEquals( + testDataInput[i], + json.encodeToString(WithChoices.serializer(), testDataOutput[i], streaming), + "failed test on ${testDataOutput[i]}, jsonTestingMode = $streaming" + ) + } + } + + interface Payment { + val amount: String + } + + @Serializable + data class SuccessfulPayment(override val amount: String, val date: String) : Payment + + @Serializable + data class RefundedPayment(override val amount: String, val date: String, val reason: String) : Payment + + object PaymentSerializer : JsonContentPolymorphicSerializer<Payment>(Payment::class) { + override fun selectDeserializer(element: JsonElement) = when { + "reason" in element.jsonObject -> RefundedPayment.serializer() + else -> SuccessfulPayment.serializer() + } + } + + @Test + fun testDocumentationSample() = parametrizedTest { streaming -> + assertEquals( + SuccessfulPayment("1.0", "03.02.2020"), + json.decodeFromString(PaymentSerializer, """{"amount":"1.0","date":"03.02.2020"}""", streaming) + ) + assertEquals( + RefundedPayment("2.0", "03.02.2020", "complaint"), + json.decodeFromString(PaymentSerializer, """{"amount":"2.0","date":"03.02.2020","reason":"complaint"}""", streaming) + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonDeserializePolymorphicTwiceTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonDeserializePolymorphicTwiceTest.kt new file mode 100644 index 00000000..f0229046 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonDeserializePolymorphicTwiceTest.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.polymorphic + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.* +import kotlin.test.* + +class JsonDeserializePolymorphicTwiceTest { + + @Serializable + sealed class Foo { + @Serializable + data class Bar(val a: Int) : Foo() + } + + @Test + fun testDeserializeTwice() { // #812 + val json = Json.encodeToJsonElement(Foo.serializer(), Foo.Bar(1)) + assertEquals(Foo.Bar(1), Json.decodeFromJsonElement(Foo.serializer(), json)) + assertEquals(Foo.Bar(1), Json.decodeFromJsonElement(Foo.serializer(), json)) + } +}
\ No newline at end of file diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonListPolymorphismTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonListPolymorphismTest.kt new file mode 100644 index 00000000..5722e8df --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonListPolymorphismTest.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.polymorphic + +import kotlinx.serialization.Polymorphic +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonTestBase +import kotlin.test.Test +import kotlin.test.assertFails + +class JsonListPolymorphismTest : JsonTestBase() { + + @Serializable + internal data class ListWrapper(val list: List<@Polymorphic InnerBase>) + + @Test + fun testPolymorphicValues() = assertJsonFormAndRestored( + ListWrapper.serializer(), + ListWrapper(listOf(InnerImpl(1), InnerImpl2(2))), + """{"list":[""" + + """{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":1,"str":"default","nullable":null},""" + + """{"type":"kotlinx.serialization.json.polymorphic.InnerImpl2","field":2}]}""", + polymorphicRelaxedJson) + + @Serializable + internal data class ListNullableWrapper(val list: List<@Polymorphic InnerBase?>) + + @Test + fun testPolymorphicNullableValues() = assertJsonFormAndRestored( + ListNullableWrapper.serializer(), + ListNullableWrapper(listOf(InnerImpl(1), null)), + """{"list":[""" + + """{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":1,"str":"default","nullable":null},""" + + "null]}", + polymorphicRelaxedJson) + + @Test + fun testPolymorphicNullableValuesWithNonNullSerializerFails() = + parametrizedTest { jsonTestingMode -> + val wrapper = ListNullableWrapper(listOf(InnerImpl(1), null)) + val serialized = polymorphicRelaxedJson.encodeToString(ListNullableWrapper.serializer(), wrapper, jsonTestingMode) + assertFails { polymorphicRelaxedJson.decodeFromString(ListWrapper.serializer(), serialized, jsonTestingMode) } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonMapPolymorphismTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonMapPolymorphismTest.kt new file mode 100644 index 00000000..b2adaa71 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonMapPolymorphismTest.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.polymorphic + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlin.test.* + +class JsonMapPolymorphismTest : JsonTestBase() { + + @Serializable + internal data class MapWrapper(val map: Map<String, @Polymorphic InnerBase>) + + @Test + fun testPolymorphicValues() = assertJsonFormAndRestored( + MapWrapper.serializer(), + MapWrapper(mapOf("k1" to InnerImpl(1), "k2" to InnerImpl2(2))), + """{"map":{"k1":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":1,"str":"default","nullable":null},"k2":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl2","field":2}}}""".trimMargin(), + polymorphicJson + ) + + @Serializable + internal data class MapNullableWrapper(val map: Map<String, @Polymorphic InnerBase?>) + + @Serializable + internal data class MapKeys(val map: Map<@Polymorphic InnerBase, String>) + + @Test + fun testPolymorphicNullableValues() = assertJsonFormAndRestored( + MapNullableWrapper.serializer(), + MapNullableWrapper(mapOf("k1" to InnerImpl(1), "k2" to null)), + """{"map":{"k1":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":1,"str":"default","nullable":null},"k2":null}}""", + polymorphicJson + ) + + @Test + fun testPolymorphicKeys() { + val json = Json { + allowStructuredMapKeys = true + serializersModule = polymorphicTestModule + encodeDefaults = true + } + assertJsonFormAndRestored( + MapKeys.serializer(), + MapKeys(mapOf(InnerImpl(1) to "k2", InnerImpl2(2) to "k2")), + """{"map":[{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":1,"str":"default","nullable":null},"k2",{"type":"kotlinx.serialization.json.polymorphic.InnerImpl2","field":2},"k2"]}""", + json + ) + } + + @Test + fun testPolymorphicKeysInArray() { + val json = Json { + allowStructuredMapKeys = true + useArrayPolymorphism = true + serializersModule = polymorphicTestModule + encodeDefaults = true + } + assertJsonFormAndRestored( + MapKeys.serializer(), + MapKeys(mapOf(InnerImpl(1) to "k2", InnerImpl2(2) to "k2")), + """{"map":[["kotlinx.serialization.json.polymorphic.InnerImpl",{"field":1,"str":"default","nullable":null}],"k2",["kotlinx.serialization.json.polymorphic.InnerImpl2",{"field":2}],"k2"]}""", + json + ) + } + + @Serializable + abstract class Base + + @Serializable + data class Derived(val myMap: Map<StringData, String>) : Base() + + @Test + fun testIssue480() { + val json = Json { + allowStructuredMapKeys = true + serializersModule = SerializersModule { + polymorphic(Base::class) { + subclass(Derived.serializer()) + } + } + } + + assertJsonFormAndRestored( + Base.serializer(), + Derived(mapOf(StringData("hi") to "hello")), + """{"type":"kotlinx.serialization.json.polymorphic.JsonMapPolymorphismTest.Derived","myMap":[{"data":"hi"},"hello"]}""", + json + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonNestedPolymorphismTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonNestedPolymorphismTest.kt new file mode 100644 index 00000000..0caa99dd --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonNestedPolymorphismTest.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.polymorphic + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlin.test.* + +class JsonNestedPolymorphismTest : JsonTestBase() { + + private val polymorphicJson = Json { + isLenient = true + encodeDefaults = true + serializersModule = SerializersModule { + polymorphic(Any::class) { + subclass(InnerImpl.serializer()) + subclass(InnerImpl2.serializer()) + subclass(OuterImpl.serializer()) + + } + + polymorphic(InnerBase::class) { + subclass(InnerImpl.serializer()) + subclass(InnerImpl2.serializer()) + } + } + } + + @Serializable + internal data class NestedGenericsList(val list: List<List<@Polymorphic Any>>) + + @Test + fun testAnyList() = assertJsonFormAndRestored( + NestedGenericsList.serializer(), + NestedGenericsList(listOf(listOf(InnerImpl(1)), listOf(InnerImpl(2)))), + """{"list":[[""" + + """{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":1,"str":"default","nullable":null}],[""" + + """{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":2,"str":"default","nullable":null}]]}""", + polymorphicJson) + + @Serializable + internal data class NestedGenericsMap(val list: Map<String, Map<String, @Polymorphic Any>>) + + @Test + fun testAnyMap() = assertJsonFormAndRestored( + NestedGenericsMap.serializer(), + NestedGenericsMap(mapOf("k1" to mapOf("k1" to InnerImpl(1)))), + """{"list":{"k1":{"k1":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl",""" + + """"field":1,"str":"default","nullable":null}}}}""", + polymorphicJson) + + @Serializable + internal data class AnyWrapper(@Polymorphic val any: Any) + + @Test + fun testAny() = assertJsonFormAndRestored( + AnyWrapper.serializer(), + AnyWrapper(OuterImpl(InnerImpl2(1), InnerImpl(2))), + """{"any":""" + + """{"type":"kotlinx.serialization.json.polymorphic.OuterImpl",""" + + """"base":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl2","field":1},""" + + """"base2":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":2,"str":"default","nullable":null}}}""", + polymorphicJson) +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonNullablePolymorphicTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonNullablePolymorphicTest.kt new file mode 100644 index 00000000..ba8d0dfe --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonNullablePolymorphicTest.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.polymorphic + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlin.test.* + +class JsonNullablePolymorphicTest : JsonTestBase() { + @Serializable + data class NullableHolder(@Polymorphic val a: Any?) + + @Serializable + @SerialName("Box") + data class Box(val i: Int) + + @Test + fun testPolymorphicNulls() { + val json = Json { + serializersModule = SerializersModule { + polymorphic(Any::class) { + subclass(Box::class) + } + } + } + + assertJsonFormAndRestored(serializer(), NullableHolder(Box(42)), """{"a":{"type":"Box","i":42}}""", json) + assertJsonFormAndRestored(serializer(), NullableHolder(null), """{"a":null}""", json) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphicClassDescriptorTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphicClassDescriptorTest.kt new file mode 100644 index 00000000..b11e9dad --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphicClassDescriptorTest.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.polymorphic + +import kotlinx.serialization.json.* +import kotlin.test.* + +class JsonPolymorphicClassDescriptorTest : JsonTestBase() { + + private val json = Json { + classDiscriminator = "class" + serializersModule = polymorphicTestModule + encodeDefaults = true + } + + @Test + fun testPolymorphicProperties() = assertJsonFormAndRestored( + InnerBox.serializer(), + InnerBox(InnerImpl(42, "foo")), + """{"base":{"class":"kotlinx.serialization.json.polymorphic.InnerImpl",""" + + """"field":42,"str":"foo","nullable":null}}""", + json + ) +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphicObjectTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphicObjectTest.kt new file mode 100644 index 00000000..e47f5790 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphicObjectTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.polymorphic + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlin.test.* + +class JsonPolymorphicObjectTest : JsonTestBase() { + + @Serializable + data class Holder(@Polymorphic val a: Any) + + @Serializable + @SerialName("MyObject") + object MyObject { + @Suppress("unused") + val unused = 42 + } + + val json = Json { + serializersModule = SerializersModule { + polymorphic(Any::class) { + subclass(MyObject::class, MyObject.serializer()) // JS bug workaround + } + } + } + + @Test + fun testRegularPolymorphism() { + assertJsonFormAndRestored(Holder.serializer(), Holder(MyObject), """{"a":{"type":"MyObject"}}""", json) + } + + @Test + fun testArrayPolymorphism() { + val json = Json(from = json) { + useArrayPolymorphism = true + } + assertJsonFormAndRestored(Holder.serializer(), Holder(MyObject), """{"a":["MyObject",{}]}""", json) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt new file mode 100644 index 00000000..b7d4f122 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.polymorphic + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.json.internal.* +import kotlinx.serialization.modules.* +import kotlinx.serialization.test.assertFailsWithSerial +import kotlin.test.* + +class JsonPolymorphismExceptionTest : JsonTestBase() { + + @Serializable + abstract class Base + + @Serializable + @SerialName("derived") + class Derived(val nested: Nested = Nested()) : Base() + + @Serializable + class Nested + + @Test + fun testDecodingException() = parametrizedTest { jsonTestingMode -> + val serialModule = SerializersModule { + polymorphic(Base::class) { + subclass(Derived::class) + } + } + + assertFailsWithSerial("JsonDecodingException") { + Json { serializersModule = serialModule }.decodeFromString(Base.serializer(), """{"type":"derived","nested":null}""", jsonTestingMode) + } + } + + @Test + fun testMissingDiscriminator() = parametrizedTest { jsonTestingMode -> + val serialModule = SerializersModule { + polymorphic(Base::class) { + subclass(Derived::class) + } + } + + assertFailsWithSerial("JsonDecodingException") { + Json { serializersModule = serialModule }.decodeFromString(Base.serializer(), """{"nested":{}}""", jsonTestingMode) + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonProhibitedPolymorphicKindsTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonProhibitedPolymorphicKindsTest.kt new file mode 100644 index 00000000..7a825395 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonProhibitedPolymorphicKindsTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.polymorphic + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlin.test.* + +class JsonProhibitedPolymorphicKindsTest : JsonTestBase() { + + @Serializable + sealed class Base { + @Serializable + class Impl(val data: Int) : Base() + } + + @Serializable + enum class MyEnum + + @Test + fun testSealedSubclass() { + assertFailsWith<IllegalArgumentException> { + Json(true) { + subclass(Base::class) + } + } + assertFailsWith<IllegalArgumentException> { + Json(false) { + subclass(Base::class) + } + } + } + + @Test + fun testPrimitive() { + assertFailsWith<IllegalArgumentException> { + Json(false) { + subclass(Int::class) + } + } + + // Doesn't fail + Json(true) { + subclass(Int::class) + } + } + + @Test + fun testEnum() { + assertFailsWith<IllegalArgumentException> { + Json(false) { + subclass(MyEnum::class) + } + } + + Json(true) { + subclass(MyEnum::class) + } + } + + @Test + fun testStructures() { + assertFailsWith<IllegalArgumentException> { + Json(false) { + subclass(serializer<Map<Int, Int>>()) + } + } + + assertFailsWith<IllegalArgumentException> { + Json(false) { + subclass(serializer<List<Int>>()) + } + } + + Json(true) { + subclass(serializer<List<Int>>()) + } + + + Json(true) { + subclass(serializer<Map<Int, Int>>()) + } + } + + private fun Json(useArrayPolymorphism: Boolean, builderAction: PolymorphicModuleBuilder<Any>.() -> Unit) = Json { + this.useArrayPolymorphism = useArrayPolymorphism + serializersModule = SerializersModule { + polymorphic(Any::class) { + builderAction() + } + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPropertyPolymorphicTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPropertyPolymorphicTest.kt new file mode 100644 index 00000000..e2e10e24 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPropertyPolymorphicTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.polymorphic + +import kotlinx.serialization.PolymorphicSerializer +import kotlinx.serialization.json.JsonTestBase +import kotlin.test.Test +import kotlin.test.assertEquals + +class JsonPropertyPolymorphicTest : JsonTestBase() { + + @Test + fun testPolymorphicProperties() = assertJsonFormAndRestored( + InnerBox.serializer(), + InnerBox(InnerImpl(42, "foo")), + """{"base":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl",""" + + """"field":42,"str":"foo","nullable":null}}""", + polymorphicRelaxedJson) + + @Test + fun testFlatPolymorphic() = parametrizedTest { jsonTestingMode -> + val base: InnerBase = InnerImpl(42, "foo") + val string = polymorphicRelaxedJson.encodeToString(PolymorphicSerializer(InnerBase::class), base, jsonTestingMode) + assertEquals("""{"type":"kotlinx.serialization.json.polymorphic.InnerImpl",""" + + """"field":42,"str":"foo","nullable":null}""", string) + assertEquals(base, polymorphicRelaxedJson.decodeFromString(PolymorphicSerializer(InnerBase::class), string, jsonTestingMode)) + } + + @Test + fun testNestedPolymorphicProperties() = assertJsonFormAndRestored( + OuterBox.serializer(), + OuterBox(OuterImpl(InnerImpl(42), InnerImpl2(42)), InnerImpl2(239)), + """{"outerBase":{""" + + """"type":"kotlinx.serialization.json.polymorphic.OuterImpl",""" + + """"base":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":42,"str":"default","nullable":null},""" + + """"base2":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl2","field":42}},""" + + """"innerBase":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl2","field":239}}""", + polymorphicRelaxedJson) + + @Test + fun testPolymorphicNullableProperties() = assertJsonFormAndRestored( + InnerNullableBox.serializer(), + InnerNullableBox(InnerImpl(42, "foo")), + """{"base":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl",""" + + """"field":42,"str":"foo","nullable":null}}""", + polymorphicRelaxedJson) + + @Test + fun testPolymorphicNullablePropertiesWithNull() = + assertJsonFormAndRestored(InnerNullableBox.serializer(), InnerNullableBox(null), """{"base":null}""", polymorphicJson) + + @Test + fun testNestedPolymorphicNullableProperties() = assertJsonFormAndRestored( + OuterNullableBox.serializer(), + OuterNullableBox(OuterNullableImpl(InnerImpl(42), null), InnerImpl2(239)), + """{"outerBase":{""" + + """"type":"kotlinx.serialization.json.polymorphic.OuterNullableImpl",""" + + """"base":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl","field":42,"str":"default","nullable":null},"base2":null},""" + + """"innerBase":{"type":"kotlinx.serialization.json.polymorphic.InnerImpl2","field":239}}""", + polymorphicRelaxedJson) +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonTreeDecoderPolymorphicTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonTreeDecoderPolymorphicTest.kt new file mode 100644 index 00000000..9d8e861d --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonTreeDecoderPolymorphicTest.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.polymorphic + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.* +import kotlin.test.* + +class JsonTreeDecoderPolymorphicTest : JsonTestBase() { + + @Serializable + sealed class Sealed + + @Serializable + data class ClassContainingItself( + val a: String, + val b: String, + val c: ClassContainingItself? = null, + val d: String? + ) : Sealed() + + val inner = ClassContainingItself( + "InnerA", + "InnerB", + null, + "InnerC" + ) + val outer = ClassContainingItself( + "OuterA", + "OuterB", + inner, + "OuterC" + ) + + @Test + fun testDecodingWhenClassContainsItself() = parametrizedTest { jsonTestingMode -> + val encoded = default.encodeToString(outer as Sealed, jsonTestingMode) + val decoded: Sealed = Json.decodeFromString(encoded, jsonTestingMode) + assertEquals(outer, decoded) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/PolymorphicClasses.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/PolymorphicClasses.kt new file mode 100644 index 00000000..e70d89c3 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/PolymorphicClasses.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.polymorphic + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlin.native.concurrent.* + +@Serializable +internal open class InnerBase + +internal interface OuterBase + +@Serializable +internal data class InnerImpl(val field: Int, val str: String = "default", val nullable: Int? = null) : InnerBase() + +@Serializable +internal data class InnerImpl2(val field: Int) : InnerBase() + +@Serializable +internal data class InnerBox(@Polymorphic val base: InnerBase) + +@Serializable +internal data class InnerNullableBox(@Polymorphic val base: InnerBase?) + +@Serializable +internal data class OuterImpl(@Polymorphic val base: InnerBase, @Polymorphic val base2: InnerBase) : OuterBase + +@Serializable +internal data class OuterNullableImpl(@Polymorphic val base: InnerBase?, @Polymorphic val base2: InnerBase?) : OuterBase + +@Serializable +internal data class OuterBox(@Polymorphic val outerBase: OuterBase, @Polymorphic val innerBase: InnerBase) + +@Serializable +internal data class OuterNullableBox(@Polymorphic val outerBase: OuterBase?, @Polymorphic val innerBase: InnerBase?) + +internal val polymorphicTestModule = SerializersModule { + polymorphic(InnerBase::class) { + subclass(InnerImpl.serializer()) + subclass(InnerImpl2.serializer()) + } + + polymorphic(OuterBase::class) { + subclass(OuterImpl.serializer()) + subclass(OuterNullableImpl.serializer()) + } +} + +internal val polymorphicJson = Json { + serializersModule = polymorphicTestModule + encodeDefaults = true +} + +internal val polymorphicRelaxedJson = Json { + isLenient = true + serializersModule = polymorphicTestModule + encodeDefaults = true +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonArraySerializerTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonArraySerializerTest.kt new file mode 100644 index 00000000..c571ec9e --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonArraySerializerTest.kt @@ -0,0 +1,113 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.serializers + +import kotlinx.serialization.json.* +import kotlinx.serialization.test.* + +import kotlin.test.* + +class JsonArraySerializerTest : JsonTestBase() { + + private val expected = "{\"array\":[1,null,[\"nested literal\"],[],{\"key\":\"value\"}]}" + private val expectedTopLevel = "[1,null,[\"nested literal\"],[],{\"key\":\"value\"}]" + + @Test + fun testJsonArray() = parametrizedTest(default) { + assertStringFormAndRestored(expected, JsonArrayWrapper(prebuiltJson()), JsonArrayWrapper.serializer()) + } + + @Test + fun testJsonArrayAsElement() = parametrizedTest(default) { + assertStringFormAndRestored(expected.replace("array", "element"), JsonElementWrapper(prebuiltJson()), JsonElementWrapper.serializer()) + } + + @Test + fun testTopLevelJsonObjectAsElement() = parametrizedTest(default) { + assertStringFormAndRestored(expectedTopLevel, prebuiltJson(), JsonElement.serializer()) + } + + @Test + fun testJsonArrayToString() { + val prebuiltJson = prebuiltJson() + val string = lenient.encodeToString(JsonArray.serializer(), prebuiltJson) + assertEquals(string, prebuiltJson.toString()) + } + + @Test + fun testMixedLiterals() = parametrizedTest { jsonTestingMode -> + val json = """[1, "2", 3, "4"]""" + val array = default.decodeFromString(JsonArray.serializer(), json, jsonTestingMode) + array.forEachIndexed { index, element -> + require(element is JsonPrimitive) + assertEquals(index % 2 == 1, element.isString) + } + } + + @Test + fun testMissingCommas() = parametrizedTest { jsonTestingMode -> + val message = "Expected end of the array or comma" + testFails("[a b c]", message, jsonTestingMode) + testFails("[ 1 2 3 ]", message, jsonTestingMode) + testFails("[null 1 null]", message, jsonTestingMode) + testFails("[1 \n 2]", message, jsonTestingMode) + } + + @Test + fun testEmptyArray() = parametrizedTest { jsonTestingMode -> + assertEquals(JsonArray(emptyList()), lenient.decodeFromString(JsonArray.serializer(), "[]", jsonTestingMode)) + assertEquals(JsonArray(emptyList()), lenient.decodeFromString(JsonArray.serializer(), "[ ]", jsonTestingMode)) + assertEquals(JsonArray(emptyList()), lenient.decodeFromString(JsonArray.serializer(), "[\n\n]", jsonTestingMode)) + assertEquals(JsonArray(emptyList()), lenient.decodeFromString(JsonArray.serializer(), "[ \t]", jsonTestingMode)) + } + + @Test + fun testWhitespaces() = parametrizedTest { jsonTestingMode -> + assertEquals( + JsonArray(listOf(1, 2, 3, 4, 5).map(::JsonPrimitive)), + lenient.decodeFromString(JsonArray.serializer(), "[1, 2, 3, \n 4, 5]", jsonTestingMode) + ) + } + + @Test + fun testExcessiveCommas() = parametrizedTest { jsonTestingMode -> + val trailing = "Unexpected trailing comma" + val leading = "Unexpected leading comma" + testFails("[a,]", trailing, jsonTestingMode) + testFails("[,1]", leading, jsonTestingMode) + testFails("[,1,]", leading, jsonTestingMode) + testFails("[,]", leading, jsonTestingMode) + testFails("[,,]", leading, jsonTestingMode) + testFails("[,,1]", leading, jsonTestingMode) + testFails("[1,,]", trailing, jsonTestingMode) + testFails("[1,,2]", trailing, jsonTestingMode) + testFails("[, ,]", leading, jsonTestingMode) + testFails("[,\n,]", leading, jsonTestingMode) + } + + private fun testFails(input: String, errorMessage: String, jsonTestingMode: JsonTestingMode) { + assertFailsWithSerial("JsonDecodingException", errorMessage) { + lenient.decodeFromString( + JsonArray.serializer(), + input, + jsonTestingMode + ) + } + } + + private fun prebuiltJson(): JsonArray { + return buildJsonArray { + add(1) + add(JsonNull) + addJsonArray { + add("nested literal") + } + addJsonArray {} + addJsonObject { + put("key", "value") + } + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonNativePrimitivesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonNativePrimitivesTest.kt new file mode 100644 index 00000000..0afbc052 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonNativePrimitivesTest.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.serializers + +import kotlinx.serialization.json.JsonTestBase +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlin.Char.* +import kotlin.test.Test + +class JsonNativePrimitivesTest : JsonTestBase() { + @Test + fun testTopLevelNativeInt() = assertJsonFormAndRestored(Int.serializer(), 42, "42", default) + + @Test + fun testTopLevelNativeString() = assertJsonFormAndRestored(String.serializer(), "42", "\"42\"", default) + + @Test + fun testTopLevelNativeChar() = assertJsonFormAndRestored(Char.serializer(), '4', "\"4\"", default) + + @Test + fun testTopLevelNativeBoolean() = assertJsonFormAndRestored(Boolean.serializer(), true, "true", default) + + @Test + fun testTopLevelNativeNullable() = + assertJsonFormAndRestored(Int.serializer().nullable, null, "null", default) +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonNullSerializerTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonNullSerializerTest.kt new file mode 100644 index 00000000..934afcac --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonNullSerializerTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.serializers + +import kotlinx.serialization.json.* +import kotlinx.serialization.test.assertFailsWithSerialMessage +import kotlinx.serialization.test.assertStringFormAndRestored +import kotlin.test.* + +class JsonNullSerializerTest : JsonTestBase() { + + @Test + fun testJsonNull() = parametrizedTest(default) { + assertStringFormAndRestored("{\"element\":null}", JsonNullWrapper(JsonNull), JsonNullWrapper.serializer()) + } + + @Test + fun testJsonNullFailure() = parametrizedTest(default) { + assertFailsWithSerialMessage("JsonDecodingException", "'null' literal") { default.decodeFromString(JsonNullWrapper.serializer(), "{\"element\":\"foo\"}", JsonTestingMode.STREAMING) } + } + + @Test + fun testJsonNullAsElement() = parametrizedTest(default) { + assertStringFormAndRestored("{\"element\":null}", JsonElementWrapper(JsonNull), JsonElementWrapper.serializer()) + } + + @Test + fun testJsonNullAsPrimitive() = parametrizedTest(default) { + assertStringFormAndRestored("{\"primitive\":null}", JsonPrimitiveWrapper(JsonNull), JsonPrimitiveWrapper.serializer()) + } + + @Test + fun testTopLevelJsonNull() = parametrizedTest { jsonTestingMode -> + val string = default.encodeToString(JsonNull.serializer(), JsonNull, jsonTestingMode) + assertEquals("null", string) + assertEquals(JsonNull, default.decodeFromString(JsonNull.serializer(), string, jsonTestingMode)) + } + + @Test + fun testTopLevelJsonNullAsElement() = parametrizedTest { jsonTestingMode -> + val string = default.encodeToString(JsonElement.serializer(), JsonNull, jsonTestingMode) + assertEquals("null", string) + assertEquals(JsonNull, default.decodeFromString(JsonElement.serializer(), string, jsonTestingMode)) + } + + @Test + fun testTopLevelJsonNullAsPrimitive() = parametrizedTest { jsonTestingMode -> + val string = default.encodeToString(JsonPrimitive.serializer(), JsonNull, jsonTestingMode) + assertEquals("null", string) + assertEquals(JsonNull, default.decodeFromString(JsonPrimitive.serializer(), string, jsonTestingMode)) + } + + @Test + fun testJsonNullToString() { + val string = default.encodeToString(JsonPrimitive.serializer(), JsonNull) + assertEquals(string, JsonNull.toString()) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonObjectSerializerTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonObjectSerializerTest.kt new file mode 100644 index 00000000..9a65effe --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonObjectSerializerTest.kt @@ -0,0 +1,125 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.serializers + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.* +import kotlinx.serialization.json.internal.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class JsonObjectSerializerTest : JsonTestBase() { + + private val expected = """{"element":{"literal":1,"nullKey":null,"nested":{"another literal":"some value"},"\\. escaped":"\\. escaped","\n new line":"\n new line"}}""" + private val expectedTopLevel = """{"literal":1,"nullKey":null,"nested":{"another literal":"some value"},"\\. escaped":"\\. escaped","\n new line":"\n new line"}""" + + @Test + fun testJsonObject() = parametrizedTest(default) { + assertStringFormAndRestored(expected, JsonObjectWrapper(prebuiltJson()), JsonObjectWrapper.serializer()) + } + + @Test + fun testJsonObjectAsElement() = parametrizedTest(default) { + assertStringFormAndRestored(expected, JsonElementWrapper(prebuiltJson()), JsonElementWrapper.serializer()) + } + + @Test + fun testTopLevelJsonObject() = parametrizedTest (default) { + assertStringFormAndRestored(expectedTopLevel, prebuiltJson(), JsonObject.serializer()) + } + + @Test + fun testTopLevelJsonObjectAsElement() = parametrizedTest (default) { + assertStringFormAndRestored(expectedTopLevel, prebuiltJson(), JsonElement.serializer()) + } + + @Test + fun testJsonObjectToString() { + val prebuiltJson = prebuiltJson() + val string = lenient.encodeToString(JsonElement.serializer(), prebuiltJson) + assertEquals(string, prebuiltJson.toString()) + } + + @Test + fun testDocumentationSample() { + val string = Json.encodeToString(JsonElement.serializer(), buildJsonObject { put("key", 1.0) }) + val literal = Json.decodeFromString(JsonElement.serializer(), string) + assertEquals(JsonObject(mapOf("key" to JsonPrimitive(1.0))), literal) + } + + @Test + fun testMissingCommas() = parametrizedTest { jsonTestingMode -> + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(JsonObject.serializer(), "{ \"1\": \"2\" \"3\":\"4\"}", jsonTestingMode) } + } + + @Test + fun testEmptyObject() = parametrizedTest { jsonTestingMode -> + assertEquals(JsonObject(emptyMap()), lenient.decodeFromString(JsonObject.serializer(), "{}", jsonTestingMode)) + assertEquals(JsonObject(emptyMap()), lenient.decodeFromString(JsonObject.serializer(), "{}", jsonTestingMode)) + assertEquals(JsonObject(emptyMap()), lenient.decodeFromString(JsonObject.serializer(), "{\n\n}", jsonTestingMode)) + assertEquals(JsonObject(emptyMap()), lenient.decodeFromString(JsonObject.serializer(), "{ \t}", jsonTestingMode)) + } + + @Test + fun testInvalidObject() = parametrizedTest { jsonTestingMode -> + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString(JsonObject.serializer(), "{\"a\":\"b\"]", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString(JsonObject.serializer(), "{", jsonTestingMode) } + if (jsonTestingMode != JsonTestingMode.JAVA_STREAMS) // Streams support dangling characters + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString(JsonObject.serializer(), "{}}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { default.decodeFromString(JsonObject.serializer(), "{]", jsonTestingMode) } + } + + @Test + fun testWhitespaces() = parametrizedTest { jsonTestingMode -> + assertEquals( + JsonObject(mapOf("1" to JsonPrimitive(2), "3" to JsonPrimitive(4), "5" to JsonPrimitive(6))), + lenient.decodeFromString(JsonObject.serializer(), "{1: 2, 3: \n 4, 5:6}", jsonTestingMode) + ) + } + + @Test + fun testExcessiveCommas() = parametrizedTest { jsonTestingMode -> + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(JsonObject.serializer(), "{\"a\":\"b\",}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(JsonObject.serializer(), "{\"a\",}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(JsonObject.serializer(), "{,\"1\":\"2\"}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(JsonObject.serializer(), "{,\"1\":\"2\",}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(JsonObject.serializer(), "{,}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(JsonObject.serializer(), "{,,}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(JsonObject.serializer(), "{,,\"1\":\"2\"}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(JsonObject.serializer(), "{\"1\":\"2\",,}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(JsonObject.serializer(), "{\"1\":\"2\",,\"2\":\"2\"}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(JsonObject.serializer(), "{, ,}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(JsonObject.serializer(), "{,\n,}", jsonTestingMode) } + } + + @Serializable + data class Holder(val a: String) + + @Test + fun testExcessiveCommasInObject() = parametrizedTest { jsonTestingMode -> + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(Holder.serializer(), "{\"a\":\"b\",}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(Holder.serializer(), "{\"a\",}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(Holder.serializer(), "{,\"a\":\"b\"}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(Holder.serializer(), "{,\"a\":\"b\",}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(Holder.serializer(), "{,}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(Holder.serializer(), "{,,}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(Holder.serializer(), "{,,\"a\":\"b\"}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(Holder.serializer(), "{\"a\":\"b\",,}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(Holder.serializer(), "{, ,}", jsonTestingMode) } + assertFailsWithSerial("JsonDecodingException") { lenient.decodeFromString(Holder.serializer(), "{,\n,}", jsonTestingMode) } + } + + private fun prebuiltJson(): JsonObject { + return buildJsonObject { + put("literal", 1) + put("nullKey", JsonNull) + putJsonObject("nested") { + put("another literal", "some value") + } + put("\\. escaped", "\\. escaped") + put("\n new line", "\n new line") + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonPrimitiveSerializerTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonPrimitiveSerializerTest.kt new file mode 100644 index 00000000..72f8a4fb --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonPrimitiveSerializerTest.kt @@ -0,0 +1,204 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.serializers + +import kotlinx.serialization.json.* +import kotlinx.serialization.test.* +import kotlin.test.* + +class JsonPrimitiveSerializerTest : JsonTestBase() { + + @Test + fun testJsonPrimitiveDouble() = parametrizedTest { jsonTestingMode -> + if (isJs()) return@parametrizedTest // JS toString numbers + + + val wrapper = JsonPrimitiveWrapper(JsonPrimitive(1.0)) + val string = default.encodeToString(JsonPrimitiveWrapper.serializer(), wrapper, jsonTestingMode) + assertEquals("{\"primitive\":1.0}", string) + assertEquals(JsonPrimitiveWrapper(JsonPrimitive(1.0)), default.decodeFromString(JsonPrimitiveWrapper.serializer(), string, jsonTestingMode)) + } + + @Test + fun testJsonPrimitiveInt() = parametrizedTest { jsonTestingMode -> + val wrapper = JsonPrimitiveWrapper(JsonPrimitive(1)) + val string = default.encodeToString(JsonPrimitiveWrapper.serializer(), wrapper, jsonTestingMode) + assertEquals("{\"primitive\":1}", string) + assertEquals(JsonPrimitiveWrapper(JsonPrimitive(1)), default.decodeFromString(JsonPrimitiveWrapper.serializer(), string, jsonTestingMode)) + } + + + @Test + fun testJsonPrimitiveString() = parametrizedTest { jsonTestingMode -> + val wrapper = JsonPrimitiveWrapper(JsonPrimitive("foo")) + val string = default.encodeToString(JsonPrimitiveWrapper.serializer(), wrapper, jsonTestingMode) + assertEquals("{\"primitive\":\"foo\"}", string) + assertEquals(JsonPrimitiveWrapper(JsonPrimitive("foo")), default.decodeFromString(JsonPrimitiveWrapper.serializer(), string, jsonTestingMode)) + } + + @Test + fun testJsonPrimitiveStringNumber() = parametrizedTest { jsonTestingMode -> + val wrapper = JsonPrimitiveWrapper(JsonPrimitive("239")) + val string = default.encodeToString(JsonPrimitiveWrapper.serializer(), wrapper, jsonTestingMode) + assertEquals("{\"primitive\":\"239\"}", string) + assertEquals(JsonPrimitiveWrapper(JsonPrimitive("239")), default.decodeFromString(JsonPrimitiveWrapper.serializer(), string, jsonTestingMode)) + } + + @Test + fun testJsonUnquotedLiteralNumbers() = parametrizedTest { jsonTestingMode -> + listOf( + "99999999999999999999999999999999999999999999999999999999999999999999999999", + "99999999999999999999999999999999999999.999999999999999999999999999999999999", + "-99999999999999999999999999999999999999999999999999999999999999999999999999", + "-99999999999999999999999999999999999999.999999999999999999999999999999999999", + "2.99792458e8", + "-2.99792458e8", + ).forEach { literalNum -> + val literalNumJson = JsonUnquotedLiteral(literalNum) + val wrapper = JsonPrimitiveWrapper(literalNumJson) + val string = default.encodeToString(JsonPrimitiveWrapper.serializer(), wrapper, jsonTestingMode) + assertEquals("{\"primitive\":$literalNum}", string, "mode:$jsonTestingMode") + assertEquals( + JsonPrimitiveWrapper(literalNumJson), + default.decodeFromString(JsonPrimitiveWrapper.serializer(), string, jsonTestingMode), + "mode:$jsonTestingMode", + ) + } + } + + @Test + fun testTopLevelPrimitive() = parametrizedTest { jsonTestingMode -> + val string = default.encodeToString(JsonPrimitive.serializer(), JsonPrimitive(42), jsonTestingMode) + assertEquals("42", string) + assertEquals(JsonPrimitive(42), default.decodeFromString(JsonPrimitive.serializer(), string)) + } + + @Test + fun testTopLevelPrimitiveAsElement() = parametrizedTest { jsonTestingMode -> + if (isJs()) return@parametrizedTest // JS toString numbers + val string = default.encodeToString(JsonElement.serializer(), JsonPrimitive(1.3), jsonTestingMode) + assertEquals("1.3", string) + assertEquals(JsonPrimitive(1.3), default.decodeFromString(JsonElement.serializer(), string, jsonTestingMode)) + } + + @Test + fun testJsonLiteralStringToString() { + val literal = JsonPrimitive("some string literal") + val string = default.encodeToString(JsonPrimitive.serializer(), literal) + assertEquals(string, literal.toString()) + } + + @Test + fun testJsonLiteralIntToString() { + val literal = JsonPrimitive(0) + val string = default.encodeToString(JsonPrimitive.serializer(), literal) + assertEquals(string, literal.toString()) + } + + @Test + fun testJsonLiterals() { + testLiteral(0L, "0") + testLiteral(0, "0") + testLiteral(0.0, "0.0") + testLiteral(0.0f, "0.0") + testLiteral(Long.MAX_VALUE, "9223372036854775807") + testLiteral(Long.MIN_VALUE, "-9223372036854775808") + testLiteral(Float.MAX_VALUE, "3.4028235E38") + testLiteral(Float.MIN_VALUE, "1.4E-45") + testLiteral(Double.MAX_VALUE, "1.7976931348623157E308") + testLiteral(Double.MIN_VALUE, "4.9E-324") + testLiteral(Int.MAX_VALUE, "2147483647") + testLiteral(Int.MIN_VALUE, "-2147483648") + } + + private fun testLiteral(number: Number, jvmExpectedString: String) { + val literal = JsonPrimitive(number) + val string = default.encodeToString(JsonPrimitive.serializer(), literal) + assertEquals(string, literal.toString()) + if (isJvm()) { // We can rely on stable double/float format only on JVM + assertEquals(string, jvmExpectedString) + } + } + + /** + * Helper function for [testJsonPrimitiveUnsignedNumbers] + * + * Asserts that an [unsigned number][actual] can be used to create a [JsonPrimitive][actualPrimitive], + * which can be decoded correctly. + * + * @param expected the expected string value of [actual] + * @param actual the unsigned number + * @param T should be an unsigned number + */ + private inline fun <reified T> assertUnsignedNumberEncoding( + expected: String, + actual: T, + actualPrimitive: JsonPrimitive, + ) { + assertEquals( + expected, + actualPrimitive.toString(), + "expect ${T::class.simpleName} $actual can be used to create a JsonPrimitive" + ) + + parametrizedTest { mode -> + assertEquals( + expected, + default.encodeToString(JsonElement.serializer(), actualPrimitive, mode), + "expect ${T::class.simpleName} primitive can be decoded", + ) + } + } + + @Test + fun testJsonPrimitiveUnsignedNumbers() { + + val expectedActualUBytes: Map<String, UByte> = mapOf( + "0" to 0u, + "1" to 1u, + "255" to UByte.MAX_VALUE, + ) + + expectedActualUBytes.forEach { (expected, actual) -> + assertUnsignedNumberEncoding(expected, actual, JsonPrimitive(actual)) + } + + val expectedActualUShorts: Map<String, UShort> = mapOf( + "0" to 0u, + "1" to 1u, + "255" to UByte.MAX_VALUE.toUShort(), + "65535" to UShort.MAX_VALUE, + ) + + expectedActualUShorts.forEach { (expected, actual) -> + assertUnsignedNumberEncoding(expected, actual, JsonPrimitive(actual)) + } + + val expectedActualUInts: Map<String, UInt> = mapOf( + "0" to 0u, + "1" to 1u, + "255" to UByte.MAX_VALUE.toUInt(), + "65535" to UShort.MAX_VALUE.toUInt(), + "4294967295" to UInt.MAX_VALUE, + ) + + expectedActualUInts.forEach { (expected, actual) -> + assertUnsignedNumberEncoding(expected, actual, JsonPrimitive(actual)) + } + + val expectedActualULongs: Map<String, ULong> = mapOf( + "0" to 0u, + "1" to 1u, + "255" to UByte.MAX_VALUE.toULong(), + "65535" to UShort.MAX_VALUE.toULong(), + "4294967295" to UInt.MAX_VALUE.toULong(), + "18446744073709551615" to ULong.MAX_VALUE, + ) + + expectedActualULongs.forEach { (expected, actual) -> + assertUnsignedNumberEncoding(expected, actual, JsonPrimitive(actual)) + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonSerializerInGenericsTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonSerializerInGenericsTest.kt new file mode 100644 index 00000000..530fc16f --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonSerializerInGenericsTest.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.serializers + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.test.assertStringFormAndRestored +import kotlin.test.* + +class JsonSerializerInGenericsTest : JsonTestBase() { + + @Serializable + data class NonTrivialClass( + val list: List<JsonElement?>, + val nullableNull: JsonNull?, + val nestedMap: Map<String, Map<String, JsonElement?>> + ) + + private val expected = "{\"list\":[42,[{\"key\":\"value\"}],null],\"nullableNull\":null,\"nestedMap\":{\"key1\":{\"nested\":{\"first\":\"second\"},\"nullable\":null}}}" + + @Test + fun testGenericsWithNulls() = parametrizedTest(default) { + assertStringFormAndRestored(expected, create(), NonTrivialClass.serializer()) + } + + private fun create(): NonTrivialClass { + return NonTrivialClass( + arrayListOf(JsonPrimitive(42), buildJsonArray { addJsonObject { put("key", "value") } }, null), + null, + mapOf("key1" to mapOf("nested" to buildJsonObject { + put("first", "second") + }, "nullable" to null)) + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonTreeTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonTreeTest.kt new file mode 100644 index 00000000..dd4d51ef --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonTreeTest.kt @@ -0,0 +1,151 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.serializers + +import kotlinx.serialization.json.* +import kotlin.test.* + +class JsonTreeTest : JsonTestBase() { + + private fun parse(input: String): JsonElement = default.decodeFromString(JsonElement.serializer(), input) + + @Test + fun testParseWithoutExceptions() { + val input = """{"a": "foo", "b": 10, "c": ["foo", 100500, {"bar": "baz"}]}""" + parse(input) + } + + @Test + fun testJsonLiteral() { + val v = JsonPrimitive("foo") + assertEquals(v, parse("\"foo\"")) + } + + @Test + fun testJsonObject() { + val input = """{"a": "foo", "b": 10, "c": true, "d": null}""" + val elem = parse(input) + + assertTrue(elem is JsonObject) + assertEquals(setOf("a", "b", "c", "d"), elem.keys) + + assertEquals(JsonPrimitive("foo"), elem["a"]) + assertEquals(10, elem["b"]?.jsonPrimitive?.int) + assertEquals(true, elem["c"]?.jsonPrimitive?.boolean) + assertSame(elem.getValue("d") as JsonNull, JsonNull) + } + + @Test + fun testJsonObjectWithArrays() { + val input = """{"a": "foo", "b": 10, "c": ["foo", 100500, {"bar": "baz"}]}""" + val elem = parse(input) + + assertTrue(elem is JsonObject) + assertEquals(setOf("a", "b", "c"), elem.keys) + assertTrue(elem.getValue("c") is JsonArray) + + val array = elem.getValue("c").jsonArray + assertEquals("foo", array.getOrNull(0)?.jsonPrimitive?.content) + assertEquals(100500, array.getOrNull(1)?.jsonPrimitive?.int) + + assertTrue(array[2] is JsonObject) + val third = array[2].jsonObject + assertEquals("baz", third.getValue("bar").jsonPrimitive.content) + } + + @Test + fun testSaveToJson() { + val input = """{"a":"foo","b":10,"c":true,"d":null,"e":["foo",100500,{"bar":"baz"}]}""" + val elem = parse(input) + val json = elem.toString() + assertEquals(input, json) + } + + @Test + fun testEqualityTest() { + val input = """{"a": "foo", "b": 10}""" + val parsed = parse(input) + val parsed2 = parse(input) + val handCrafted = buildJsonObject { put("a", JsonPrimitive("foo")); put("b", JsonPrimitive(10)) } + assertEquals(parsed, parsed2) + assertEquals(parsed, handCrafted) + } + + @Test + fun testInEqualityTest() { + val input = """{"a": "10", "b": 10}""" + val parsed = parse(input) as JsonObject + val handCrafted = buildJsonObject { put("a", JsonPrimitive("10")); put("b", JsonPrimitive(10)) } + assertEquals(parsed, handCrafted) + + assertNotEquals(parsed["a"], parsed["b"]) + assertNotEquals(parsed["a"], handCrafted["b"]) + assertNotEquals(handCrafted["a"], parsed["b"]) + assertNotEquals(handCrafted["a"], handCrafted["b"]) + } + + @Test + fun testExceptionalState() { + val tree = + JsonObject(mapOf("a" to JsonPrimitive(42), "b" to JsonArray(listOf(JsonNull)), "c" to JsonPrimitive(false))) + assertFailsWith<NoSuchElementException> { tree.getValue("no key").jsonObject } + assertFailsWith<IllegalArgumentException> { tree.getValue("a").jsonArray } + assertEquals(null, tree["no key"]?.jsonObject) + assertEquals(null, tree["a"] as? JsonArray) + + val n = tree.getValue("b").jsonArray[0].jsonPrimitive + assertFailsWith<NumberFormatException> { n.int } + assertEquals(null, n.intOrNull) + + assertFailsWith<IllegalStateException> { n.boolean } + assertEquals(null, n.booleanOrNull) + } + + @Test + fun testThatJsonArraysCompareWithLists() { + val jsonArray: List<JsonElement> = JsonArray(listOf(JsonPrimitive(3), JsonPrimitive(4))) + val arrayList: List<JsonElement> = ArrayList(listOf(JsonPrimitive(3), JsonPrimitive(4))) + val otherArrayList: List<JsonElement> = ArrayList(listOf(JsonPrimitive(3), JsonPrimitive(5))) + + assertEquals(jsonArray, arrayList) + assertEquals(arrayList, jsonArray) + assertEquals(jsonArray.hashCode(), arrayList.hashCode()) + assertNotEquals(jsonArray, otherArrayList) + } + + @Test + fun testThatJsonObjectsCompareWithMaps() { + val jsonObject: Map<String, JsonElement> = JsonObject( + mapOf( + "three" to JsonPrimitive(3), + "four" to JsonPrimitive(4) + ) + ) + val hashMap: Map<String, JsonElement> = HashMap( + mapOf( + "three" to JsonPrimitive(3), + "four" to JsonPrimitive(4) + ) + ) + val otherJsonObject: Map<String, JsonElement> = JsonObject( + mapOf( + "three" to JsonPrimitive(3), + "five" to JsonPrimitive(5) + ) + ) + val otherHashMap: Map<String, JsonElement> = HashMap( + mapOf( + "three" to JsonPrimitive(3), + "four" to JsonPrimitive(5) + ) + ) + + assertEquals(jsonObject, hashMap) + assertEquals(hashMap, jsonObject) + assertEquals(jsonObject.hashCode(), hashMap.hashCode()) + assertNotEquals(jsonObject, otherHashMap) + assertNotEquals(jsonObject, otherJsonObject) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonUnquotedLiteralTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonUnquotedLiteralTest.kt new file mode 100644 index 00000000..e8090044 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonUnquotedLiteralTest.kt @@ -0,0 +1,140 @@ +package kotlinx.serialization.json.serializers + +import kotlinx.serialization.builtins.MapSerializer +import kotlinx.serialization.json.* +import kotlinx.serialization.test.assertFailsWithSerialMessage +import kotlin.test.Test +import kotlin.test.assertEquals + +class JsonUnquotedLiteralTest : JsonTestBase() { + + private fun assertUnquotedLiteralEncoded(inputValue: String) { + val unquotedElement = JsonUnquotedLiteral(inputValue) + + assertEquals( + inputValue, + unquotedElement.toString(), + "expect JsonElement.toString() returns the unquoted input value" + ) + + parametrizedTest { mode -> + assertEquals(inputValue, default.encodeToString(JsonElement.serializer(), unquotedElement, mode)) + } + } + + @Test + fun testUnquotedJsonNumbers() { + assertUnquotedLiteralEncoded("1") + assertUnquotedLiteralEncoded("-1") + assertUnquotedLiteralEncoded("100.0") + assertUnquotedLiteralEncoded("-100.0") + + assertUnquotedLiteralEncoded("9999999999999999999999999999999999999999999999999999999.9999999999999999999999999999999999999999999999999999999") + assertUnquotedLiteralEncoded("-9999999999999999999999999999999999999999999999999999999.9999999999999999999999999999999999999999999999999999999") + + assertUnquotedLiteralEncoded("99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999") + assertUnquotedLiteralEncoded("-99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999") + + assertUnquotedLiteralEncoded("2.99792458e8") + assertUnquotedLiteralEncoded("-2.99792458e8") + + assertUnquotedLiteralEncoded("2.99792458E8") + assertUnquotedLiteralEncoded("-2.99792458E8") + + assertUnquotedLiteralEncoded("11.399999999999") + assertUnquotedLiteralEncoded("0.30000000000000004") + assertUnquotedLiteralEncoded("0.1000000000000000055511151231257827021181583404541015625") + } + + @Test + fun testUnquotedJsonWhitespaceStrings() { + assertUnquotedLiteralEncoded("") + assertUnquotedLiteralEncoded(" ") + assertUnquotedLiteralEncoded("\t") + assertUnquotedLiteralEncoded("\t\t\t") + assertUnquotedLiteralEncoded("\r\n") + assertUnquotedLiteralEncoded("\n") + assertUnquotedLiteralEncoded("\n\n\n") + } + + @Test + fun testUnquotedJsonStrings() { + assertUnquotedLiteralEncoded("lorem") + assertUnquotedLiteralEncoded(""""lorem"""") + assertUnquotedLiteralEncoded( + """ + Well, my name is Freddy Kreuger + I've got the Elm Street blues + I've got a hand like a knife rack + And I die in every film! + """.trimIndent() + ) + } + + @Test + fun testUnquotedJsonObjects() { + assertUnquotedLiteralEncoded("""{"some":"json"}""") + assertUnquotedLiteralEncoded("""{"some":"json","object":true,"count":1,"array":[1,2.0,-333,"4",boolean]}""") + } + + @Test + fun testUnquotedJsonArrays() { + assertUnquotedLiteralEncoded("""[1,2,3]""") + assertUnquotedLiteralEncoded("""["a","b","c"]""") + assertUnquotedLiteralEncoded("""[true,false]""") + assertUnquotedLiteralEncoded("""[1,2.0,-333,"4",boolean]""") + assertUnquotedLiteralEncoded("""[{"some":"json","object":true,"count":1,"array":[1,2.0,-333,"4",boolean]}]""") + assertUnquotedLiteralEncoded("""[{"some":"json","object":true,"count":1,"array":[1,2.0,-333,"4",boolean]},{"some":"json","object":true,"count":1,"array":[1,2.0,-333,"4",boolean]}]""") + } + + @Test + fun testUnquotedJsonNull() { + assertEquals(JsonNull, JsonUnquotedLiteral(null)) + } + + @Test + fun testUnquotedJsonNullString() { + fun test(block: () -> Unit) { + assertFailsWithSerialMessage( + exceptionName = "JsonEncodingException", + message = "Creating a literal unquoted value of 'null' is forbidden. If you want to create JSON null literal, use JsonNull object, otherwise, use JsonPrimitive", + block = block, + ) + } + + test { JsonUnquotedLiteral("null") } + test { JsonUnquotedLiteral(JsonNull.content) } + test { buildJsonObject { put("key", JsonUnquotedLiteral("null")) } } + } + + @Test + fun testUnquotedJsonInvalidMapKeyIsEscaped() { + val mapSerializer = MapSerializer( + JsonPrimitive.serializer(), + JsonPrimitive.serializer(), + ) + + fun test(expected: String, input: String) = parametrizedTest { mode -> + val data = mapOf(JsonUnquotedLiteral(input) to JsonPrimitive("invalid key")) + + assertEquals( + """ {"$expected":"invalid key"} """.trim(), + default.encodeToString(mapSerializer, data, mode), + ) + } + + test(" ", " ") + test( + """ \\\"\\\" """.trim(), + """ \"\" """.trim(), + ) + test( + """ \\\\\\\" """.trim(), + """ \\\" """.trim(), + ) + test( + """ {\\\"I'm not a valid JSON object key\\\"} """.trim(), + """ {\"I'm not a valid JSON object key\"} """.trim(), + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/Primitives.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/Primitives.kt new file mode 100644 index 00000000..34c6dc88 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/Primitives.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json.serializers + +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +@Serializable +data class JsonElementWrapper(val element: JsonElement) + +@Serializable +data class JsonPrimitiveWrapper(val primitive: JsonPrimitive) + +@Serializable +data class JsonNullWrapper(val element: JsonNull) + +@Serializable +data class JsonObjectWrapper(val element: JsonObject) + +@Serializable +data class JsonArrayWrapper(val array: JsonArray)
\ No newline at end of file diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionInSealedClassesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionInSealedClassesTest.kt new file mode 100644 index 00000000..18a49a52 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionInSealedClassesTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.modules + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlin.test.* + +private const val prefix = "kotlinx.serialization.modules.SerialNameCollisionInSealedClassesTest" + +class SerialNameCollisionInSealedClassesTest { + @Serializable + sealed class Base { + @Serializable + data class Child(val type: String, @SerialName("type2") val f: String = "2") : Base() + } + + private fun Json(discriminator: String, useArrayPolymorphism: Boolean = false) = Json { + classDiscriminator = discriminator + this.useArrayPolymorphism = useArrayPolymorphism + } + + @Test + fun testCollisionWithDiscriminator() { + assertFailsWith<IllegalStateException> { Json("type").encodeToString(Base.serializer(), Base.Child("a")) } + assertFailsWith<IllegalStateException> { Json("type2").encodeToString(Base.serializer(), Base.Child("a")) } + Json("f").encodeToString(Base.serializer(), Base.Child("a")) + } + + @Test + fun testNoCollisionWithArrayPolymorphism() { + Json("type", true).encodeToString(Base.serializer(), Base.Child("a")) + } + + @Serializable + sealed class BaseCollision { + @Serializable + class Child() : BaseCollision() + + @Serializable + @SerialName("$prefix.BaseCollision.Child") + class ChildCollided() : BaseCollision() + } + + @Test + fun testDescriptorInitializerFailure() { + BaseCollision.Child() + BaseCollision.ChildCollided() + BaseCollision.ChildCollided.serializer().descriptor // Doesn't fail + assertFailsWith<IllegalStateException> { BaseCollision.serializer().descriptor } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionTest.kt new file mode 100644 index 00000000..4a88cb12 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/modules/SerialNameCollisionTest.kt @@ -0,0 +1,124 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.modules + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlin.test.* + +private const val prefix = "kotlinx.serialization.modules.SerialNameCollisionTest" + +class SerialNameCollisionTest { + + // Polymorphism + interface IBase + + @Serializable + abstract class Base : IBase + + @Serializable + data class Derived(val type: String, val type2: String) : Base() + + @Serializable + data class DerivedCustomized( + @SerialName("type") val t: String, @SerialName("type2") val t2: String, val t3: String + ) : Base() + + @Serializable + @SerialName("$prefix.Derived") + data class DerivedRenamed(val type: String, val type2: String) : Base() + + private fun Json(discriminator: String, context: SerializersModule, useArrayPolymorphism: Boolean = false) = Json { + classDiscriminator = discriminator + this.useArrayPolymorphism = useArrayPolymorphism + serializersModule = context + + } + + @Test + fun testCollisionWithDiscriminator() { + val module = SerializersModule { + polymorphic(Base::class) { + subclass(Derived.serializer()) + } + } + + assertFailsWith<IllegalArgumentException> { Json("type", module) } + assertFailsWith<IllegalArgumentException> { Json("type2", module) } + Json("type3", module) // OK + } + + @Test + fun testNoCollisionWithArrayPolymorphism() { + val module = SerializersModule { + polymorphic(Base::class) { + subclass(Derived.serializer()) + } + } + Json("type", module, true) + } + + @Test + fun testCollisionWithDiscriminatorViaSerialNames() { + val module = SerializersModule { + polymorphic(Base::class) { + subclass(DerivedCustomized.serializer()) + } + } + + assertFailsWith<IllegalArgumentException> { Json("type", module) } + assertFailsWith<IllegalArgumentException> { Json("type2", module) } + assertFailsWith<IllegalArgumentException> { Json("t3", module) } + Json("t4", module) // OK + + } + + @Test + fun testCollisionWithinHierarchy() { + SerializersModule { + assertFailsWith<IllegalArgumentException> { + polymorphic(Base::class) { + subclass(Derived.serializer()) + subclass(DerivedRenamed.serializer()) + } + } + } + } + + @Test + fun testCollisionWithinHierarchyViaConcatenation() { + val module = SerializersModule { + polymorphic(Base::class) { + subclass(Derived.serializer()) + } + } + val module2 = SerializersModule { + polymorphic(Base::class) { + subclass(DerivedRenamed.serializer()) + } + } + + assertFailsWith<IllegalArgumentException> { module + module2 } + } + + @Test + fun testNoCollisionWithinHierarchy() { + val module = SerializersModule { + polymorphic(Base::class) { + subclass(Derived.serializer()) + } + + polymorphic(IBase::class) { + subclass(DerivedRenamed.serializer()) + } + } + + assertSame(Derived.serializer(), module.getPolymorphic(Base::class, "$prefix.Derived")) + assertSame( + DerivedRenamed.serializer(), + module.getPolymorphic(IBase::class, "$prefix.Derived") + ) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/test/ContextualTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/test/ContextualTest.kt new file mode 100644 index 00000000..0b34f1c7 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/test/ContextualTest.kt @@ -0,0 +1,47 @@ +@file:UseContextualSerialization(ContextualTest.Cont::class) + +package kotlinx.serialization.test + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseContextualSerialization +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encodeToString +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.contextual + +class ContextualTest { + data class Cont(val i: Int) + + @Serializable + data class DateHolder(val cont: Cont?) + + object DateSerializer: KSerializer<Cont> { + override fun deserialize(decoder: Decoder): Cont { + return Cont(decoder.decodeInt()) + } + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ContSerializer", PrimitiveKind.INT) + + override fun serialize(encoder: Encoder, value: Cont) { + encoder.encodeInt(value.i) + } + + } + + val module = SerializersModule { + contextual(DateSerializer) + } + + @kotlin.test.Test + fun test() { + val json = Json { serializersModule = module } + + println(json.encodeToString(DateHolder(Cont(42)))) + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/test/CurrentPlatform.common.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/test/CurrentPlatform.common.kt new file mode 100644 index 00000000..8c3633b4 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/test/CurrentPlatform.common.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.test + +enum class Platform { + JVM, JS, NATIVE, WASM +} + +public expect val currentPlatform: Platform + +public fun isJs(): Boolean = currentPlatform == Platform.JS +public fun isJvm(): Boolean = currentPlatform == Platform.JVM +public fun isNative(): Boolean = currentPlatform == Platform.NATIVE +public fun isWasm(): Boolean = currentPlatform == Platform.WASM
\ No newline at end of file diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/test/InternalHexConverter.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/test/InternalHexConverter.kt new file mode 100644 index 00000000..349eb43c --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/test/InternalHexConverter.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.test + +object InternalHexConverter { + private const val hexCode = "0123456789ABCDEF" + + fun parseHexBinary(s: String): ByteArray { + val len = s.length + require(len % 2 == 0) { "HexBinary string must be even length" } + val bytes = ByteArray(len / 2) + var i = 0 + + while (i < len) { + val h = hexToInt(s[i]) + val l = hexToInt(s[i + 1]) + require(!(h == -1 || l == -1)) { "Invalid hex chars: ${s[i]}${s[i+1]}" } + + bytes[i / 2] = ((h shl 4) + l).toByte() + i += 2 + } + + return bytes + } + + private fun hexToInt(ch: Char): Int = when (ch) { + in '0'..'9' -> ch - '0' + in 'A'..'F' -> ch - 'A' + 10 + in 'a'..'f' -> ch - 'a' + 10 + else -> -1 + } + + fun printHexBinary(data: ByteArray, lowerCase: Boolean = false): String { + val r = StringBuilder(data.size * 2) + for (b in data) { + r.append(hexCode[b.toInt() shr 4 and 0xF]) + r.append(hexCode[b.toInt() and 0xF]) + } + return if (lowerCase) r.toString().lowercase() else r.toString() + } + + fun toHexString(n: Int): String { + val arr = ByteArray(4) + for (i in 0 until 4) { + arr[i] = (n shr (24 - i * 8)).toByte() + } + return printHexBinary(arr, true).trimStart('0').takeIf { it.isNotEmpty() } ?: "0" + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/test/JsonHelpers.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/test/JsonHelpers.kt new file mode 100644 index 00000000..f3b742e3 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/test/JsonHelpers.kt @@ -0,0 +1,9 @@ +package kotlinx.serialization.test + +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.json.Json + +public expect fun <T> Json.encodeViaStream(serializer: SerializationStrategy<T>, value: T): String + +public expect fun <T> Json.decodeViaStream(serializer: DeserializationStrategy<T>, input: String): T diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/test/TestHelpers.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/test/TestHelpers.kt new file mode 100644 index 00000000..27ac19f1 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/test/TestHelpers.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") + +package kotlinx.serialization.test + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.json.internal.ESCAPE_STRINGS +import kotlin.random.Random +import kotlin.random.nextInt +import kotlin.test.* + +fun SerialDescriptor.assertDescriptorEqualsTo(other: SerialDescriptor) { + assertEquals(serialName, other.serialName) + assertEquals(elementsCount, other.elementsCount) + assertEquals(isNullable, other.isNullable) + assertEquals(annotations, other.annotations) + assertEquals(kind, other.kind) + for (i in 0 until elementsCount) { + getElementDescriptor(i).assertDescriptorEqualsTo(other.getElementDescriptor(i)) + val name = getElementName(i) + val otherName = other.getElementName(i) + assertEquals(name, otherName) + assertEquals(getElementAnnotations(i), other.getElementAnnotations(i)) + assertEquals(name, otherName) + assertEquals(isElementOptional(i), other.isElementOptional(i)) + } +} + +inline fun noJs(test: () -> Unit) { + if (!isJs()) test() +} + +inline fun jvmOnly(test: () -> Unit) { + if (isJvm()) test() +} + +inline fun assertFailsWithMissingField(block: () -> Unit) { + val e = assertFailsWith<SerializationException>(block = block) + assertTrue(e.message?.contains("but it was missing") ?: false) +} + +fun generateRandomUnicodeString(size: Int): String { + return buildString(size) { + repeat(size) { + val pickEscape = Random.nextBoolean() + if (pickEscape) { + // Definitely an escape symbol + append(ESCAPE_STRINGS.random().takeIf { it != null } ?: 'N') + } else { + // Any symbol, including escaping one + append(Char(Random.nextInt(Char.MIN_VALUE.code..Char.MAX_VALUE.code)).takeIf { it.isDefined() && !it.isSurrogate()} ?: 'U') + } + } + } +} diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/test/TestId.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/test/TestId.kt new file mode 100644 index 00000000..c4af25e5 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/test/TestId.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.test + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* + +@SerialInfo +@Target(AnnotationTarget.PROPERTY) +annotation class Id(val id: Int) diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/test/TestingFramework.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/test/TestingFramework.kt new file mode 100644 index 00000000..3ec07149 --- /dev/null +++ b/formats/json-tests/commonTest/src/kotlinx/serialization/test/TestingFramework.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.test + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlin.test.* + + +inline fun <reified T : Any> assertStringFormAndRestored( + expected: String, + original: T, + serializer: KSerializer<T>, + format: StringFormat = Json, + printResult: Boolean = false +) { + val string = format.encodeToString(serializer, original) + if (printResult) println("[Serialized form] $string") + assertEquals(expected, string) + val restored = format.decodeFromString(serializer, string) + if (printResult) println("[Restored form] $restored") + assertEquals(original, restored) +} + +inline fun <reified T : Any> StringFormat.assertStringFormAndRestored( + expected: String, + original: T, + serializer: KSerializer<T>, + printResult: Boolean = false +) { + val string = this.encodeToString(serializer, original) + if (printResult) println("[Serialized form] $string") + assertEquals(expected, string) + val restored = this.decodeFromString(serializer, string) + if (printResult) println("[Restored form] $restored") + assertEquals(original, restored) +} + +fun <T : Any> assertSerializedAndRestored( + original: T, + serializer: KSerializer<T>, + format: StringFormat = Json, + printResult: Boolean = false +) { + if (printResult) println("[Input] $original") + val string = format.encodeToString(serializer, original) + if (printResult) println("[Serialized form] $string") + val restored = format.decodeFromString(serializer, string) + if (printResult) println("[Restored form] $restored") + assertEquals(original, restored) +} + +inline fun assertFailsWithSerial( + exceptionName: String, + assertionMessage: String? = null, + block: () -> Unit +) { + val exception = assertFailsWith(SerializationException::class, assertionMessage, block) + assertEquals( + exceptionName, + exception::class.simpleName, + "Expected exception with type '${exceptionName}' but got '${exception::class.simpleName}'" + ) +} +inline fun assertFailsWithSerialMessage( + exceptionName: String, + message: String, + assertionMessage: String? = null, + block: () -> Unit +) { + val exception = assertFailsWith(SerializationException::class, assertionMessage, block) + assertEquals( + exceptionName, + exception::class.simpleName, + "Expected exception type '$exceptionName' but actual is '${exception::class.simpleName}'" + ) + assertTrue( + exception.message!!.contains(message), + "expected:<$message> but was:<${exception.message}>" + ) +} +inline fun <reified T : Throwable> assertFailsWithMessage( + message: String, + assertionMessage: String? = null, + block: () -> Unit +) { + val exception = assertFailsWith(T::class, assertionMessage, block) + assertTrue( + exception.message!!.contains(message), + "expected:<$message> but was:<${exception.message}>" + ) +} + +inline fun checkSerializationException(action: () -> Unit, assertions: SerializationException.(String) -> Unit) { + val e = assertFailsWith(SerializationException::class, action) + assertNotNull(e.message) + e.assertions(e.message!!) +} diff --git a/formats/json-tests/jsTest/src/kotlinx/serialization/json/DecodeFromDynamicSpecialCasesTest.kt b/formats/json-tests/jsTest/src/kotlinx/serialization/json/DecodeFromDynamicSpecialCasesTest.kt new file mode 100644 index 00000000..748a2dce --- /dev/null +++ b/formats/json-tests/jsTest/src/kotlinx/serialization/json/DecodeFromDynamicSpecialCasesTest.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlin.test.* + +class DecodeFromDynamicSpecialCasesTest { + + @Test + fun testTopLevelInt() { + val dyn = js("42") + val parsed = Json.decodeFromDynamic<Int>(dyn) + assertEquals(42, parsed) + } + + @Test + fun testTopLevelString() { + val dyn = js(""""42"""") + val parsed = Json.decodeFromDynamic<String>(dyn) + assertEquals("42", parsed) + } + + @Test + fun testTopLevelList() { + val dyn = js("[1, 2, 3]") + val parsed = Json.decodeFromDynamic<List<Int>>(dyn) + assertEquals(listOf(1, 2, 3), parsed) + } + + @Test + fun testStringMap() = testMapWithPrimitiveKey("1", "2") + + @Test + fun testByteMap() = testMapWithPrimitiveKey(1.toByte(), 2.toByte()) + + @Test + fun testCharMap() = testMapWithPrimitiveKey('1', '2') + + @Test + fun testShortMap() = testMapWithPrimitiveKey(1.toShort(), 2.toShort()) + + @Test + fun testIntMap() = testMapWithPrimitiveKey(1, 2) + + @Test + fun testLongMap() = testMapWithPrimitiveKey(1L, 2L) + + @Test + fun testDoubleMap() = testMapWithPrimitiveKey(1.0, 2.0) + + @Test + fun testFloatMap() = testMapWithPrimitiveKey(1.0f, 2.0f) + + private inline fun <reified T> testMapWithPrimitiveKey(k1: T, k2: T) { + val map = mapOf(k1 to 3, k2 to 4) + val dyn = js("{1:3, 2:4}") + val parsed = Json.decodeFromDynamic<Map<T, Int>>(dyn) + assertEquals(map, parsed) + } + + @Test + fun testJsonPrimitive() { + testJsonElement<JsonPrimitive>(js("42"), JsonPrimitive(42)) + testJsonElement<JsonElement>(js("42"), JsonPrimitive(42)) + } + + @Test + fun testJsonPrimitiveDouble() { + testJsonElement<JsonElement>(js("42.0"), JsonPrimitive(42.0)) + testJsonElement<JsonPrimitive>(js("42.0"), JsonPrimitive(42.0)) + } + + @Test + fun testJsonStringPrimitive() { + testJsonElement<JsonElement>(js(""""42""""), JsonPrimitive("42")) + testJsonElement<JsonPrimitive>(js(""""42""""), JsonPrimitive("42")) + } + + @Test + fun testJsonNull() { + testJsonElement<JsonElement>(js("null"), JsonNull) + testJsonElement<JsonElement>(js("undefined"), JsonNull) + } + + @Test + fun testJsonArray() { + testJsonElement<JsonElement>(js("[1,2,3]"), JsonArray((1..3).map(::JsonPrimitive))) + testJsonElement<JsonArray>(js("[1,2,3]"), JsonArray((1..3).map(::JsonPrimitive))) + } + + @Test + fun testJsonObject() { + testJsonElement<JsonElement>( + js("""{1:2,"3":4}"""), + JsonObject(mapOf("1" to JsonPrimitive(2), "3" to JsonPrimitive(4))) + ) + testJsonElement<JsonObject>( + js("""{1:2,"3":4}"""), + JsonObject(mapOf("1" to JsonPrimitive(2), "3" to JsonPrimitive(4))) + ) + } + + private inline fun <reified T : JsonElement> testJsonElement(js: dynamic, expected: JsonElement) { + val parsed = Json.decodeFromDynamic<T>(js) + assertEquals(expected, parsed) + } + + @Serializable + data class Wrapper(val e: JsonElement, val p: JsonPrimitive, val o: JsonObject, val a: JsonArray, val n: JsonNull) + + @Test + fun testJsonElementWrapper() { + val js = js("""{"e":42,"p":"239", "o":{"k":"v"}, "a":[1, 2, 3], "n": null}""") + val parsed = Json.decodeFromDynamic<Wrapper>(js) + val expected = Wrapper(JsonPrimitive(42), JsonPrimitive("239"), buildJsonObject { put("k", "v") }, JsonArray((1..3).map(::JsonPrimitive)), JsonNull) + assertEquals(expected, parsed) + } +} diff --git a/formats/json-tests/jsTest/src/kotlinx/serialization/json/DecodeFromDynamicTest.kt b/formats/json-tests/jsTest/src/kotlinx/serialization/json/DecodeFromDynamicTest.kt new file mode 100644 index 00000000..1a0c29c2 --- /dev/null +++ b/formats/json-tests/jsTest/src/kotlinx/serialization/json/DecodeFromDynamicTest.kt @@ -0,0 +1,200 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.* +import kotlinx.serialization.modules.* +import kotlinx.serialization.test.* +import kotlin.test.* +import kotlin.test.assertFailsWith + +class DecodeFromDynamicTest { + @Serializable + data class Data(val a: Int) + + @Serializable + data class DataWrapper(val s: String, val d: Data?) + + @Serializable + data class DataWrapperOptional(val s: String,val d: Data? = null) + + @Serializable + data class IntList(val l: List<Int>) + + @Serializable + data class ListOfLists(val l: List<List<Data>>) + + @Serializable + data class MapWrapper(val m: Map<String, Int>) + + @Serializable + data class ComplexMapWrapper(val m: Map<String, Data>) + + @Serializable + data class IntMapWrapper(val m: Map<Int, Int>) + + @Serializable + data class WithChar(val a: Char) + + @Serializable + data class AllTypes( + val b: Byte, + val s: Short, + val i: Int, + val f: Float, + val d: Double, + val c: Char, + val B: Boolean, + val S: String + ) + + @Serializable + data class NonTrivialMap(val m: Map<String, Char>) + + data class NotDefault(val a: Int) + + object NDSerializer : KSerializer<NotDefault> { + override val descriptor = buildClassSerialDescriptor("notDefault") { + element<Int>("a") + } + + override fun serialize(encoder: Encoder, value: NotDefault) { + encoder.encodeInt(value.a) + } + + override fun deserialize(decoder: Decoder): NotDefault { + return NotDefault(decoder.decodeInt()) + } + } + + @Serializable + data class NDWrapper(@Contextual val data: NotDefault) + + @Test + fun dynamicSimpleTest() { + val dyn = js("{a: 42}") + val parsed = Json.decodeFromDynamic(Data.serializer(), dyn) + assertEquals(Data(42), parsed) + + val dyn2 = js("{a: 'a'}") + val parsed2 = Json.decodeFromDynamic(WithChar.serializer(), dyn2) + assertEquals(WithChar('a'), parsed2) + + val dyn3 = js("{a: 97}") + val parsed3 = Json.decodeFromDynamic(WithChar.serializer(), dyn3) + assertEquals(WithChar('a'), parsed3) + } + + @Test + fun dynamicAllTypesTest() { + val dyn = js("""{ b: 1, s: 2, i: 3, f: 1.0, d: 42.0, c: 'a', B: true, S: "str"}""") + val kotlinObj = AllTypes(1, 2, 3, 1.0f, 42.0, 'a', true, "str") + + assertEquals(kotlinObj, Json.decodeFromDynamic(AllTypes.serializer(), dyn)) + } + + @Test + fun dynamicNestedTest() { + val dyn = js("""{s:"foo", d:{a:42}}""") + val parsed = Json.decodeFromDynamic(DataWrapper.serializer(), dyn) + val expected = DataWrapper("foo", Data(42)) + assertEquals(expected, parsed) + assertEquals(3, parsed.s.length) + assertFailsWith(ClassCastException::class) { dyn as DataWrapper } + } + + @Test + fun dynamicNullableTest() { + val dyn1 = js("""({s:"foo", d: null})""") + val dyn2 = js("""({s:"foo"})""") + val dyn3 = js("""({s:"foo", d: undefined})""") + + assertEquals(DataWrapper("foo", null), Json.decodeFromDynamic(DataWrapper.serializer(), dyn1)) + assertFailsWithMissingField { + Json.decodeFromDynamic( + DataWrapper.serializer(), + dyn2 + ) + } + assertFailsWithMissingField { + Json.decodeFromDynamic( + DataWrapper.serializer(), + dyn3 + ) + } + } + + @Test + fun dynamicOptionalTest() { + val dyn1 = js("""({s:"foo", d: null})""") + val dyn2 = js("""({s:"foo"})""") + val dyn3 = js("""({s:"foo", d: undefined})""") + + assertEquals( + DataWrapperOptional("foo", null), + Json.decodeFromDynamic(DataWrapperOptional.serializer(), dyn1) + ) + assertEquals( + DataWrapperOptional("foo", null), + Json.decodeFromDynamic(DataWrapperOptional.serializer(), dyn2) + ) + assertEquals( + DataWrapperOptional("foo", null), + Json.decodeFromDynamic(DataWrapperOptional.serializer(), dyn3) + ) + } + + @Test + fun dynamicListTest() { + val dyn1 = js("""({l:[1,2]})""") + val dyn2 = js("""({l:[[],[{a:42}]]})""") + + assertEquals(IntList(listOf(1, 2)), Json.decodeFromDynamic(IntList.serializer(), dyn1)) + assertEquals( + ListOfLists(listOf(listOf(), listOf(Data(42)))), + Json.decodeFromDynamic(ListOfLists.serializer(), dyn2) + ) + } + + @Test + fun dynamicMapTest() { + val dyn = js("({m : {\"a\": 1, \"b\" : 2}})") + val m = MapWrapper(mapOf("a" to 1, "b" to 2)) + assertEquals(m, Json.decodeFromDynamic(MapWrapper.serializer(), dyn)) + } + + @Test + fun testFunnyMap() { + val dyn = js("({m: {\"a\": 'b', \"b\" : 'a'}})") + val m = NonTrivialMap(mapOf("a" to 'b', "b" to 'a')) + assertEquals(m, Json.decodeFromDynamic(NonTrivialMap.serializer(), dyn)) + } + + @Test + fun dynamicMapComplexTest() { + val dyn = js("({m: {1: {a: 42}, 2: {a: 43}}})") + val m = ComplexMapWrapper(mapOf("1" to Data(42), "2" to Data(43))) + assertEquals(m, Json.decodeFromDynamic(ComplexMapWrapper.serializer(), dyn)) + } + + @Test + fun testIntMapTest() { + val dyn = js("({m: {1: 2, 3: 4}})") + val m = IntMapWrapper(mapOf(1 to 2, 3 to 4)) + assertEquals(m, Json.decodeFromDynamic(IntMapWrapper.serializer(), dyn)) + } + + @Test + fun parseWithCustomSerializers() { + val json = Json { serializersModule = serializersModuleOf(NotDefault::class, NDSerializer) } + val dyn1 = js("({data: 42})") + assertEquals(NDWrapper(NotDefault(42)), + json.decodeFromDynamic(NDWrapper.serializer(), dyn1) + ) + } + +} diff --git a/formats/json-tests/jsTest/src/kotlinx/serialization/json/DynamicPolymorphismTest.kt b/formats/json-tests/jsTest/src/kotlinx/serialization/json/DynamicPolymorphismTest.kt new file mode 100644 index 00000000..3ff05ba0 --- /dev/null +++ b/formats/json-tests/jsTest/src/kotlinx/serialization/json/DynamicPolymorphismTest.kt @@ -0,0 +1,324 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.polymorphic +import kotlinx.serialization.modules.subclass +import kotlin.test.Test +import kotlin.test.assertEquals + +class DynamicPolymorphismTest { + @Serializable + sealed class Sealed(val intField: Int) { + @Serializable + @SerialName("object") + object ObjectChild : Sealed(0) + + @Serializable + @SerialName("data_class") + data class DataClassChild(val name: String) : Sealed(1) + + @Serializable + @SerialName("type_child") + data class TypeChild(val type: String) : Sealed(2) + + @Serializable + @SerialName("nullable_child") + data class NullableChild(val nullable: String?): Sealed(3) + + @Serializable + @SerialName("list_child") + data class ListChild(val list: List<String>): Sealed(4) + + @Serializable + @SerialName("default_child") + data class DefaultChild(val default: String? = "default"): Sealed(5) + } + + @Serializable + @JsonClassDiscriminator("sealed_custom") + sealed class SealedCustom { + @Serializable + @SerialName("data_class") + data class DataClassChild(val name: String) : SealedCustom() + } + + @Serializable + data class CompositeClass(val mark: String, val nested: Sealed) + + @Serializable + data class AnyWrapper(@Polymorphic val any: Any) + + @Serializable + @SerialName("string_wrapper") + data class StringWrapper(val text: String) + + private val arrayJson = Json { + useArrayPolymorphism = true + } + + private val objectJson = Json { + useArrayPolymorphism = false + } + + @Test + fun testDiscriminatorName() { + val newClassDiscriminator = "key" + + val json = Json { + useArrayPolymorphism = false + classDiscriminator = newClassDiscriminator + } + + val value = Sealed.TypeChild("discriminator-test") + encodeAndDecode(Sealed.serializer(), value, json) { + assertEquals("type_child", this[newClassDiscriminator]) + assertEquals(value.type, this.type) + assertEquals(value.intField, this.intField) + assertEquals(3, fieldsCount(this)) + } + } + + @Test + fun testCustomClassDiscriminator() { + val value = SealedCustom.DataClassChild("custom-discriminator-test") + encodeAndDecode(SealedCustom.serializer(), value, objectJson) { + assertEquals("data_class", this["sealed_custom"]) + assertEquals(undefined, this.type) + assertEquals(2, fieldsCount(this)) + } + } + + @Test + fun testComposite() { + val nestedValue = Sealed.DataClassChild("child") + val value = CompositeClass("composite", nestedValue) + encodeAndDecode(CompositeClass.serializer(), value, objectJson) { + assertEquals(value.mark, this.mark) + val nested = this.nested + assertEquals("data_class", nested.type) + assertEquals(nestedValue.name, nested.name) + assertEquals(nestedValue.intField, nested.intField) + assertEquals(3, fieldsCount(nested)) + } + + encodeAndDecode(CompositeClass.serializer(), value, arrayJson) { + assertEquals(value.mark, this.mark) + assertEquals("data_class", this.nested[0]) + val nested = this.nested[1] + assertEquals(nestedValue.name, nested.name) + assertEquals(nestedValue.intField, nested.intField) + assertEquals(2, fieldsCount(nested)) + } + } + + + @Test + fun testDataClass() { + val value = Sealed.DataClassChild("data-class") + + encodeAndDecode(Sealed.serializer(), value, objectJson) { + assertEquals("data_class", this.type) + assertEquals(value.name, this.name) + assertEquals(value.intField, this.intField) + assertEquals(3, fieldsCount(this)) + } + + encodeAndDecode(Sealed.serializer(), value, arrayJson) { + assertEquals("data_class", this[0]) + val dynamicValue = this[1] + assertEquals(value.name, dynamicValue.name) + assertEquals(value.intField, dynamicValue.intField) + assertEquals(2, fieldsCount(dynamicValue)) + } + } + + @Test + fun testNullable() { + val nonNullChild = Sealed.NullableChild("nonnull") + encodeAndDecode(Sealed.serializer(), nonNullChild, arrayJson) { + assertEquals("nullable_child", this[0]) + val dynamicValue = this[1] + assertEquals(nonNullChild.nullable, dynamicValue.nullable) + assertEquals(nonNullChild.intField, dynamicValue.intField) + assertEquals(2, fieldsCount(dynamicValue)) + } + encodeAndDecode(Sealed.serializer(), nonNullChild, objectJson) { + assertEquals("nullable_child", this.type) + assertEquals(nonNullChild.nullable, this.nullable) + assertEquals(nonNullChild.intField, this.intField) + assertEquals(3, fieldsCount(this)) + } + + val nullChild = Sealed.NullableChild(null) + encodeAndDecode(Sealed.serializer(), nullChild, arrayJson) { + assertEquals("nullable_child", this[0]) + val dynamicValue = this[1] + assertEquals(nullChild.nullable, dynamicValue.nullable) + assertEquals(nullChild.intField, dynamicValue.intField) + assertEquals(2, fieldsCount(dynamicValue)) + } + encodeAndDecode(Sealed.serializer(), nullChild, objectJson) { + assertEquals("nullable_child", this.type) + assertEquals(nullChild.nullable, this.nullable) + assertEquals(nullChild.intField, this.intField) + assertEquals(3, fieldsCount(this)) + } + } + + @Test + fun testList() { + val listChild = Sealed.ListChild(listOf("one", "two")) + encodeAndDecode(Sealed.serializer(), listChild, arrayJson) { + assertEquals("list_child", this[0]) + val dynamicValue = this[1] + assertEquals(listChild.list, (dynamicValue.list as Array<String>).toList()) + assertEquals(listChild.intField, dynamicValue.intField) + assertEquals(2, fieldsCount(dynamicValue)) + } + encodeAndDecode(Sealed.serializer(), listChild, objectJson) { + assertEquals("list_child", this.type) + assertEquals(listChild.list, (this.list as Array<String>).toList()) + assertEquals(listChild.intField, this.intField) + assertEquals(3, fieldsCount(this)) + } + } + + @Test + fun testEmptyList() { + val emptyListChild = Sealed.ListChild(emptyList()) + encodeAndDecode(Sealed.serializer(), emptyListChild, arrayJson) { + assertEquals("list_child", this[0]) + val dynamicValue = this[1] + assertEquals(emptyListChild.list, (dynamicValue.list as Array<String>).toList()) + assertEquals(emptyListChild.intField, dynamicValue.intField) + assertEquals(2, fieldsCount(dynamicValue)) + } + encodeAndDecode(Sealed.serializer(), emptyListChild, objectJson) { + assertEquals("list_child", this.type) + assertEquals(emptyListChild.list, (this.list as Array<String>).toList()) + assertEquals(emptyListChild.intField, this.intField) + assertEquals(3, fieldsCount(this)) + } + } + + @Test + fun testDefaultValue() { + val objectJsonWithDefaults = Json(objectJson) { + encodeDefaults = true + } + + val arrayJsonWithDefaults = Json(arrayJson) { + encodeDefaults = true + } + + val defaultChild = Sealed.DefaultChild() + encodeAndDecode(Sealed.serializer(), defaultChild, arrayJson) { + assertEquals("default_child", this[0]) + val dynamicValue = this[1] + assertEquals(null, dynamicValue.default, "arrayJson should not encode defaults") + assertEquals(defaultChild.intField, dynamicValue.intField) + assertEquals(1, fieldsCount(dynamicValue)) + } + encodeAndDecode(Sealed.serializer(), defaultChild, arrayJsonWithDefaults) { + assertEquals("default_child", this[0]) + val dynamicValue = this[1] + assertEquals(defaultChild.default, dynamicValue.default, "arrayJsonWithDefaults should encode defaults") + assertEquals(defaultChild.intField, dynamicValue.intField) + assertEquals(2, fieldsCount(dynamicValue)) + } + + encodeAndDecode(Sealed.serializer(), defaultChild, objectJson) { + assertEquals("default_child", this.type) + assertEquals(null, this.default, "objectJson should not encode defaults") + assertEquals(defaultChild.intField, this.intField) + assertEquals(2, fieldsCount(this)) + } + encodeAndDecode(Sealed.serializer(), defaultChild, objectJsonWithDefaults) { + assertEquals("default_child", this.type) + assertEquals(defaultChild.default, this.default, "objectJsonWithDefaults should encode defaults") + assertEquals(defaultChild.intField, this.intField) + assertEquals(3, fieldsCount(this)) + } + + } + + @Test + fun testNonDefaultValue() { + val nonDefaultChild = Sealed.DefaultChild("non default value") + encodeAndDecode(Sealed.serializer(), nonDefaultChild, arrayJson) { + assertEquals("default_child", this[0]) + val dynamicValue = this[1] + assertEquals(nonDefaultChild.default, dynamicValue.default) + assertEquals(nonDefaultChild.intField, dynamicValue.intField) + assertEquals(2, fieldsCount(dynamicValue)) + } + + encodeAndDecode(Sealed.serializer(), nonDefaultChild, objectJson) { + assertEquals("default_child", this.type) + assertEquals(nonDefaultChild.default, this.default) + assertEquals(nonDefaultChild.intField, this.intField) + assertEquals(3, fieldsCount(this)) + } + } + + @Test + fun testObject() { + val value = Sealed.ObjectChild + encodeAndDecode(Sealed.serializer(), value, objectJson) { + assertEquals("object", this.type) + assertEquals(1, fieldsCount(this)) + } + + encodeAndDecode(Sealed.serializer(), value, arrayJson) { + assertEquals("object", this[0]) + assertEquals(0, fieldsCount(this[1])) + } + } + + @Test + fun testAny() { + val serializersModule = SerializersModule { + polymorphic(Any::class) { + subclass(StringWrapper.serializer()) + } + } + + val json = Json(objectJson) { this.serializersModule = serializersModule } + + val anyValue = StringWrapper("some text") + val value = AnyWrapper(anyValue) + + encodeAndDecode(AnyWrapper.serializer(), value, json) { + assertEquals("string_wrapper", this.any.type) + assertEquals(anyValue.text, this.any.text) + assertEquals(1, fieldsCount(this)) + assertEquals(2, fieldsCount(this.any)) + } + + val json2 = Json(arrayJson) { this.serializersModule = serializersModule } + + encodeAndDecode(AnyWrapper.serializer(), value, json2) { + assertEquals("string_wrapper", this.any[0]) + assertEquals(anyValue.text, this.any[1].text) + assertEquals(1, fieldsCount(this)) + assertEquals(2, fieldsCount(this.any)) + } + } + + @Suppress("NOTHING_TO_INLINE") + private inline fun fieldsCount(dynamic: dynamic): Int { + return js("Object").keys(dynamic).length as Int + } + + private fun <T> encodeAndDecode(deserializer: KSerializer<T>, value: T, json: Json, assertBlock: dynamic.() -> Unit) { + val dynamic = json.encodeToDynamic(deserializer, value) + assertBlock(dynamic) + val decodedValue = json.decodeFromDynamic(deserializer, dynamic) + assertEquals(value, decodedValue) + } +} diff --git a/formats/json-tests/jsTest/src/kotlinx/serialization/json/DynamicToLongTest.kt b/formats/json-tests/jsTest/src/kotlinx/serialization/json/DynamicToLongTest.kt new file mode 100644 index 00000000..2daf0bb2 --- /dev/null +++ b/formats/json-tests/jsTest/src/kotlinx/serialization/json/DynamicToLongTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlin.test.* + +/** + * [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER] + */ +internal const val MAX_SAFE_INTEGER: Double = 9007199254740991.toDouble() // 2^53 - 1 + +class DynamicToLongTest { + + @Serializable + data class HasLong(val l: Long) + + private fun test(dynamic: dynamic, expectedResult: Result<Long>) { + val parsed = kotlin.runCatching { Json.decodeFromDynamic(HasLong.serializer(), dynamic).l } + assertEquals(expectedResult.isSuccess, parsed.isSuccess, "Results are different") + parsed.onSuccess { assertEquals(expectedResult.getOrThrow(), it) } + // to compare without message + parsed.onFailure { assertSame(expectedResult.exceptionOrNull()!!::class, it::class) } + } + + private fun shouldFail(dynamic: dynamic) = test(dynamic, Result.failure(SerializationException(""))) + + @Test + fun canParseNotSoBigLongs() { + test(js("{l:1}"), Result.success(1)) + test(js("{l:0}"), Result.success(0)) + test(js("{l:-1}"), Result.success(-1)) + } + + @Test + fun ignoresIncorrectValues() { + shouldFail(js("{l:0.5}")) + shouldFail(js("{l: Math.PI}")) + shouldFail(js("{l: NaN}")) + shouldFail(js("""{l: "a string"}""")) + shouldFail(js("{l:Infinity}")) + shouldFail(js("{l:+Infinity}")) + shouldFail(js("{l:-Infinity}")) + } + + @Test + fun handlesEdgyValues() { + test(js("{l:Number.MAX_SAFE_INTEGER}"), Result.success(MAX_SAFE_INTEGER.toLong())) + test(js("{l:Number.MAX_SAFE_INTEGER - 1}"), Result.success(MAX_SAFE_INTEGER.toLong() - 1)) + test(js("{l:-Number.MAX_SAFE_INTEGER}"), Result.success(-MAX_SAFE_INTEGER.toLong())) + shouldFail(js("{l: Number.MAX_SAFE_INTEGER + 1}")) + shouldFail(js("{l: Number.MAX_SAFE_INTEGER + 2}")) + shouldFail(js("{l: -Number.MAX_SAFE_INTEGER - 1}")) + shouldFail(js("{l: 2e100}")) + shouldFail(js("{l: 2e100 + 1}")) + test(js("{l: Math.pow(2, 53) - 1}"), Result.success(MAX_SAFE_INTEGER.toLong())) + } +} diff --git a/formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicSpecialCasesTest.kt b/formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicSpecialCasesTest.kt new file mode 100644 index 00000000..e4190644 --- /dev/null +++ b/formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicSpecialCasesTest.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlin.test.* + +class EncodeToDynamicSpecialCasesTest { + + @Test + fun testTopLevelInt() = assertDynamicForm(42) + + @Test + fun testTopLevelString() = assertDynamicForm("42") + + @Test + fun testTopLevelList() = assertDynamicForm(listOf(1, 2, 3)) + + @Test + fun testStringMap() = assertDynamicForm(mapOf("1" to 2, "3" to 4)) + + @Test + fun testByteMap() = assertDynamicForm(mapOf(1.toByte() to 2, 3.toByte() to 4)) + + @Test + fun testCharMap() = assertDynamicForm(mapOf('1' to 2, '3' to 4)) + + @Test + fun testShortMap() = assertDynamicForm(mapOf(1.toShort() to 2, 3.toShort() to 4)) + + @Test + fun testIntMap() = assertDynamicForm(mapOf(1 to 2, 3 to 4)) + + @Test + fun testLongMap() = assertDynamicForm(mapOf(1L to 2, 3L to 4)) + + @Test + fun testDoubleMap() = assertDynamicForm(mapOf(1.0 to 2, 3.0 to 4)) + + @Test + fun testFloatMap() = assertDynamicForm(mapOf(1.0f to 2, 3.0f to 4)) + + @Test + fun testJsonPrimitive() { + assertDynamicForm(JsonPrimitive(42)) + assertDynamicForm<JsonElement>(JsonPrimitive(42)) + } + + @Test + fun testJsonPrimitiveDouble() { + assertDynamicForm<JsonElement>(JsonPrimitive(42.0)) + assertDynamicForm<JsonPrimitive>(JsonPrimitive(42.0)) + } + + @Test + fun testJsonStringPrimitive() { + assertDynamicForm<JsonElement>(JsonPrimitive("42")) + assertDynamicForm<JsonPrimitive>(JsonPrimitive("42")) + } + + @Test + fun testJsonArray() { + assertDynamicForm<JsonElement>(JsonArray((1..3).map(::JsonPrimitive))) + assertDynamicForm<JsonArray>(JsonArray((1..3).map(::JsonPrimitive))) + } + + @Test + fun testJsonObject() { + assertDynamicForm<JsonElement>( + JsonObject(mapOf("1" to JsonPrimitive(2), "3" to JsonPrimitive(4))) + ) + assertDynamicForm<JsonObject>( + JsonObject(mapOf("1" to JsonPrimitive(2), "3" to JsonPrimitive(4))) + ) + } + + + @Serializable + data class Wrapper(val e: JsonElement, val p: JsonPrimitive, val o: JsonObject, val a: JsonArray) + + @Test + fun testJsonElementWrapper() { + assertDynamicForm(Wrapper(JsonPrimitive(42), JsonPrimitive("239"), buildJsonObject { put("k", "v") }, JsonArray((1..3).map(::JsonPrimitive)))) + } +} diff --git a/formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt b/formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt new file mode 100644 index 00000000..74196b7b --- /dev/null +++ b/formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt @@ -0,0 +1,380 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.builtins.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.internal.* +import kotlinx.serialization.* +import kotlinx.serialization.modules.* +import kotlin.test.* + +@Suppress("UnsafeCastFromDynamic") +class EncodeToDynamicTest { + @Serializable + data class Data(val a: Int) + + @Serializable + open class DataWrapper(open val s: String, val d: Data? = Data(1)) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class.js != other::class.js) return false + + other as DataWrapper + + if (s != other.s) return false + if (d != other.d) return false + + return true + } + + override fun hashCode(): Int { + var result = s.hashCode() + result = 31 * result + (d?.hashCode() ?: 0) + return result + } + } + + @Serializable + data class NestedList(val a: String, val list: List<Int>) + + @Serializable + data class ListOfLists(val l: List<List<Data>>) + + @Serializable + data class MapWrapper(val m: Map<String?, Int>) + + @Serializable + data class ComplexMapWrapper(val m: Map<String, Data>) + + @Serializable + data class WithChar(val a: Char) + + @Serializable + data class WithLong(val l: Long) + + @Serializable + data class AllTypes( + val b: Byte, + val s: Short, + val i: Int, + val f: Float, + val d: Double, + val c: Char, + val B: Boolean, + val S: String + ) + + @Serializable + data class EnumWrapper(val e: Color) + + @Serializable + sealed class Sealed { + @Serializable + data class One(val string: String) : Sealed() + } + + @Serializable + class WithJsName(@JsName("b") val a: String) + + @Serializable + data class WithSerialName(@SerialName("b") val a: String) + + @Serializable + enum class Color { + RED, + GREEN, + + @SerialName("red") + WITH_SERIALNAME_red + } + + @Serializable(MyFancyClass.Companion::class) + data class MyFancyClass(val value: String) { + + companion object : KSerializer<MyFancyClass> { + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("MyFancyClass", PrimitiveKind.STRING) + override fun serialize(encoder: Encoder, value: MyFancyClass) { + encoder.encodeString("fancy ${value.value}") + } + + override fun deserialize(decoder: Decoder): MyFancyClass { + return MyFancyClass(decoder.decodeString().removePrefix("fancy ")) + } + } + } + + @Test + fun dynamicSimpleTest() { + assertDynamicForm(Data(42)) { data, serialized -> + assertEquals(data.a, serialized.a) + } + + assertDynamicForm(WithChar('c')) { data, serialized -> + assertEquals(data.a.toString(), serialized.a) + } + + assertDynamicForm(AllTypes(1, 2, 3, 4.0f, 5.0, 'c', true, "string")) + + + assertDynamicForm(WithLong(5L)) + assertDynamicForm(WithLong(MAX_SAFE_INTEGER.toLong())) + assertDynamicForm(WithLong(MAX_SAFE_INTEGER.unaryMinus().toLong())) + } + + @Test + fun wrappedObjectsTest() { + assertDynamicForm(DataWrapper("a string", Data(42))) { data, serialized -> + assertEquals(data.s, serialized.s) + assertNotNull(serialized.d) + assertEquals(data.d?.a, serialized.d.a) + } + } + + @Test + fun listTest() { + assertDynamicForm(listOf(1, 2, 3, 44), serializer = ListSerializer(Int.serializer())) { data, serialized -> + assertNotNull(serialized.length, "length property should exist") + assertEquals(data.size, serialized.length) + + for (i in data.indices) { + assertEquals(data[i], serialized[i]) + } + } + } + + @Test + fun arrayTest() { + assertDynamicForm(intArrayOf(1, 2, 3, 44), serializer = IntArraySerializer(), true) { data, serialized -> + assertNotNull(serialized.length, "length property should exist") + assertEquals(data.size, serialized.length) + + for (i in data.indices) { + assertEquals(data[i], serialized[i]) + } + } + } + + @Test + fun nestedListTest() { + assertDynamicForm(NestedList("a string", listOf(1, 2, 3, 44))) { data, serialized -> + assertEquals(data.a, serialized.a) + assertNotNull(serialized.list.length, "length property should exist") + assertEquals(data.list.size, serialized.list.length) + + for (i in data.list.indices) { + assertEquals(data.list[i], serialized.list[i]) + } + } + + } + + @Test + fun complexMapWrapperTest() { + assertDynamicForm(ComplexMapWrapper(mapOf("key1" to Data(1), "key2" to Data(2)))) + } + + @Test + fun mapWrapperTest() { + assertDynamicForm(MapWrapper(mapOf("key1" to 1, "key2" to 2))) + } + + @Test + fun listOfListsTest() { + assertDynamicForm( + ListOfLists( + listOf( + listOf(Data(11), Data(12), Data(13)), + listOf(Data(21), Data(22)) + ) + ) + ) { data, serialized -> + assertEquals(data.l.size, serialized.l.length) + assertEquals(data.l.first().size, serialized.l[0].length) + } + } + + @Serializable + data class NestedCollections(val data: Map<String, Map<String, List<Int>>>) + + @Test + fun nestedCollections() { + assertDynamicForm( + NestedCollections( + mapOf( + "one" to mapOf("oneone" to listOf(11, 12, 13), "onetwo" to listOf(1)), + "two" to mapOf("twotwo" to listOf(22, 23)) + ) + ) + , serializer = NestedCollections.serializer() + ) + } + + @Test + fun enums() { + assertDynamicForm(EnumWrapper(Color.RED)) + assertDynamicForm(Color.GREEN) + assertDynamicForm(Color.WITH_SERIALNAME_red) { _, serialized -> + assertEquals("red", serialized) + } + } + + @Test + fun singlePrimitiveValue() { + assertDynamicForm("some string") + assertDynamicForm(1.toByte()) + assertDynamicForm(1.toShort()) + assertDynamicForm(1) + assertDynamicForm(1.toFloat()) + assertDynamicForm(1.toDouble()) + assertDynamicForm('c') + assertDynamicForm(true) + assertDynamicForm(false) + assertDynamicForm(1L) + val result = Json.encodeToDynamic(String.serializer().nullable, null) + assertEquals(null, result) + } + + @Test + fun sealed() { + // test of sealed class but not polymorphic serialization + assertDynamicForm(Sealed.One("one")) + } + + @Test + fun withSerialNam() { + assertDynamicForm(WithSerialName("something")) { data, serialized -> + assertEquals(data.a, serialized.b) + } + } + + @Test + fun mapWithNullKey() { + val serialized = Json.encodeToDynamic( + MapSerializer(String.serializer().nullable, Int.serializer()), + mapOf(null to 0, "a" to 1) + ) + assertNotNull(serialized[null], "null key should be present in output") + } + + @Test + fun mapWithSimpleKey() { + + inline fun <reified T> assertSimpleMapForm(key: T, value: String) { + assertDynamicForm(mapOf(key to value), MapSerializer(serializer(), String.serializer())) + } + + assertSimpleMapForm(1, "Int 1") + assertSimpleMapForm("s", "String s") + assertSimpleMapForm('c', "char c") + assertSimpleMapForm(2.toByte(), "Byte 2") + assertSimpleMapForm(3.toShort(), "Short 3") + assertSimpleMapForm(4.toLong(), "Long 4") + assertSimpleMapForm(5.toFloat(), "Float 5") + assertSimpleMapForm(6.toDouble(), "Double 6") + + assertDynamicForm( + mapOf( + Color.RED to "RED", + Color.GREEN to "GREEN", + Color.WITH_SERIALNAME_red to "red" + ), + MapSerializer(Color.serializer(), String.serializer()) + ) { _, serialized -> + assertNotNull(serialized["red"], "WITH_SERIALNAME_red should be serialized as 'red'") + } + } + + @Test + fun mapWithIllegalKey() { + + val exception = assertFails { + Json.encodeToDynamic( + MapSerializer(Data.serializer(), String.serializer()), + mapOf( + Data(1) to "data", + Data(2) to "data", + Data(3) to "data" + ) + ) + } + assertEquals(IllegalArgumentException::class, exception::class) + assertTrue("should have a helpful error message") { + exception.message?.contains("can't be used in json as map key") == true + } + + assertFails { + @Suppress("CAST_NEVER_SUCCEEDS") + assertDynamicForm( + mapOf( + (null as? Data) to "Data null" + ), + MapSerializer(Data.serializer().nullable, String.serializer()) + ) + } + + + val doubleSerializer = MapSerializer(Double.serializer(), String.serializer()) + val value = mapOf(0.5 to "0.5") + var ex = assertFails { + assertDynamicForm(value, doubleSerializer) + } + assertTrue("should have a helpful error message") { + ex.message?.contains("can't be used in json as map key") == true + } + + ex = assertFails { + assertDynamicForm(mapOf(Double.NaN to "NaN"), doubleSerializer) + } + assertTrue("should have a helpful error message") { + ex.message?.contains("can't be used in json as map key") == true + } + + ex = assertFails { + assertDynamicForm(mapOf(Double.NEGATIVE_INFINITY to "NaN"), doubleSerializer) + } + assertTrue("should have a helpful error message") { + ex.message?.contains("can't be used in json as map key") == true + } + + assertDynamicForm(mapOf(11.0 to "11"), doubleSerializer) + + } + + @Test + fun customSerializerTest() { + assertDynamicForm(MyFancyClass("apple"), MyFancyClass.serializer()) { _, serialized -> + assertEquals("fancy apple", serialized) + } + + assertDynamicForm( + mapOf(MyFancyClass("apple") to "value"), + MapSerializer(MyFancyClass.serializer(), String.serializer()) + ) { _, serialized -> + assertNotNull(serialized["fancy apple"], "should contain key 'fancy apple'") + } + } +} + +public inline fun <reified T : Any> assertDynamicForm( + data: T, + serializer: KSerializer<T> = EmptySerializersModule().serializer(), + skipEqualsCheck: Boolean = false, + noinline assertions: ((T, dynamic) -> Unit)? = null +) { + val serialized = Json.encodeToDynamic(serializer, data) + assertions?.invoke(data, serialized) + val string = Json.encodeToString(serializer, data) + assertEquals( + string, + JSON.stringify(serialized), + "JSON.stringify representation must be the same" + ) + + if (skipEqualsCheck) return // arrays etc. + assertEquals(data, Json.decodeFromString(serializer, string)) +} diff --git a/formats/json-tests/jsTest/src/kotlinx/serialization/json/JsonCoerceInputValuesDynamicTest.kt b/formats/json-tests/jsTest/src/kotlinx/serialization/json/JsonCoerceInputValuesDynamicTest.kt new file mode 100644 index 00000000..00053297 --- /dev/null +++ b/formats/json-tests/jsTest/src/kotlinx/serialization/json/JsonCoerceInputValuesDynamicTest.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlin.test.* + +class JsonCoerceInputValuesDynamicTest { + val json = Json { + coerceInputValues = true + isLenient = true + } + + private fun <T> doTest(inputs: List<dynamic>, expected: T, serializer: KSerializer<T>) { + for (input in inputs) { + assertEquals(expected, json.decodeFromDynamic(serializer, input), "Failed on input: $input") + } + } + + @Test + fun testUseDefaultOnNonNullableBooleanDynamic() = doTest( + listOf( + js("""{"b":false}"""), + js("""{"b":null}"""), + js("""{}"""), + ), + JsonCoerceInputValuesTest.WithBoolean(), + JsonCoerceInputValuesTest.WithBoolean.serializer() + ) + + @Test + fun testUseDefaultOnUnknownEnum() { + doTest( + listOf( + js("""{"e":"unknown_value"}"""), + js("""{"e":null}"""), + js("""{}"""), + ), + JsonCoerceInputValuesTest.WithEnum(), + JsonCoerceInputValuesTest.WithEnum.serializer() + ) + assertFailsWith<SerializationException> { + json.decodeFromDynamic( + JsonCoerceInputValuesTest.WithEnum.serializer(), + js("""{"e":{"x":"definitely not a valid enum value"}}""") + ) + } + } + + @Test + fun testUseDefaultInMultipleCases() { + val testData = mapOf<dynamic, JsonCoerceInputValuesTest.MultipleValues>( + Pair( + js("""{"data":{"data":"foo"},"data2":null,"i":null,"e":null,"foo":"bar"}"""), + JsonCoerceInputValuesTest.MultipleValues( + StringData("foo"), + foo = "bar" + ) + ), + Pair( + js("""{"data":{"data":"foo"},"data2":{"intV":42},"i":null,"e":null,"foo":"bar"}"""), + JsonCoerceInputValuesTest.MultipleValues( + StringData( + "foo" + ), IntData(42), foo = "bar" + ) + ), + Pair( + js("""{"data":{"data":"foo"},"data2":{"intV":42},"i":0,"e":"NoOption","foo":"bar"}"""), + JsonCoerceInputValuesTest.MultipleValues( + StringData("foo"), + IntData(42), + i = 0, + foo = "bar" + ) + ), + Pair( + js("""{"data":{"data":"foo"},"data2":{"intV":42},"i":0,"e":"OptionC","foo":"bar"}"""), + JsonCoerceInputValuesTest.MultipleValues( + StringData("foo"), + IntData(42), + i = 0, + e = SampleEnum.OptionC, + foo = "bar" + ) + ), + ) + for ((input, expected) in testData) { + assertEquals( + expected, + json.decodeFromDynamic(JsonCoerceInputValuesTest.MultipleValues.serializer(), input), + "Failed on input: $input" + ) + } + } +} diff --git a/formats/json-tests/jsTest/src/kotlinx/serialization/json/JsonDynamicImplicitNullsTest.kt b/formats/json-tests/jsTest/src/kotlinx/serialization/json/JsonDynamicImplicitNullsTest.kt new file mode 100644 index 00000000..1191e3c9 --- /dev/null +++ b/formats/json-tests/jsTest/src/kotlinx/serialization/json/JsonDynamicImplicitNullsTest.kt @@ -0,0 +1,14 @@ +package kotlinx.serialization.json + +import kotlinx.serialization.KSerializer + +class JsonDynamicImplicitNullsTest : AbstractJsonImplicitNullsTest() { + override fun <T> Json.encode(value: T, serializer: KSerializer<T>): String { + return JSON.stringify(encodeToDynamic(serializer, value)) + } + + override fun <T> Json.decode(json: String, serializer: KSerializer<T>): T { + val x: dynamic = JSON.parse(json) + return decodeFromDynamic(serializer, x) + } +} diff --git a/formats/json-tests/jsTest/src/kotlinx/serialization/json/JsonNamesDynamicTest.kt b/formats/json-tests/jsTest/src/kotlinx/serialization/json/JsonNamesDynamicTest.kt new file mode 100644 index 00000000..0c519fc2 --- /dev/null +++ b/formats/json-tests/jsTest/src/kotlinx/serialization/json/JsonNamesDynamicTest.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.features.* +import kotlinx.serialization.test.* +import kotlin.test.* + + +class JsonNamesDynamicTest { + private val inputString1 = js("""{"foo":"foo"}""") + private val inputString2 = js("""{"_foo":"foo"}""") + + private fun parameterizedCoercingTest(test: (json: Json, msg: String) -> Unit) { + for (coercing in listOf(true, false)) { + val json = Json { + coerceInputValues = coercing + useAlternativeNames = true + } + + test( + json, + "Failed test with coercing=$coercing" + ) + } + } + + @Test + fun testParsesAllAlternativeNamesDynamic() { + for (input in listOf(inputString1, inputString2)) { + parameterizedCoercingTest { json, msg -> + val data = json.decodeFromDynamic(JsonNamesTest.WithNames.serializer(), input) + assertEquals("foo", data.data, msg + "and input '$input'") + } + } + } + + @Test + fun testEnumSupportsAlternativeNames() { + val input = js("""{"enumList":["VALUE_A", "someValue", "some_value", "VALUE_B"], "checkCoercion":"someValue"}""") + val expected = JsonNamesTest.WithEnumNames( + listOf( + JsonNamesTest.AlternateEnumNames.VALUE_A, + JsonNamesTest.AlternateEnumNames.VALUE_A, + JsonNamesTest.AlternateEnumNames.VALUE_A, + JsonNamesTest.AlternateEnumNames.VALUE_B + ), JsonNamesTest.AlternateEnumNames.VALUE_A + ) + parameterizedCoercingTest { json, msg -> + assertEquals(expected, json.decodeFromDynamic(input), msg) + } + } + + @Test + fun topLevelEnumSupportAlternativeNames() { + parameterizedCoercingTest { json, msg -> + assertEquals(JsonNamesTest.AlternateEnumNames.VALUE_A, json.decodeFromDynamic(js("\"someValue\"")), msg) + } + } + + @Test + fun testThrowsAnErrorOnDuplicateNames2() { + val serializer = JsonNamesTest.CollisionWithAlternate.serializer() + parameterizedCoercingTest { json, _ -> + assertFailsWithMessage<SerializationException>( + """The suggested name '_foo' for property foo is already one of the names for property data""", + "Class ${serializer.descriptor.serialName} did not fail" + ) { + json.decodeFromDynamic( + serializer, inputString2, + ) + } + } + } + +} diff --git a/formats/json-tests/jsTest/src/kotlinx/serialization/json/JsonNamingStrategyDynamicTest.kt b/formats/json-tests/jsTest/src/kotlinx/serialization/json/JsonNamingStrategyDynamicTest.kt new file mode 100644 index 00000000..a1f7b0e6 --- /dev/null +++ b/formats/json-tests/jsTest/src/kotlinx/serialization/json/JsonNamingStrategyDynamicTest.kt @@ -0,0 +1,39 @@ +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.features.* +import kotlin.test.* + +class JsonNamingStrategyDynamicTest: JsonTestBase() { + private val jsForm = js("""{"simple":"a","one_word":"b","already_in_snake":"c","a_lot_of_words":"d","first_capitalized":"e","has_acronym_url":"BAZ","has_digit123_and_postfix":"QUX","coercion_test":"QUX"}""") + private val jsFormNeedsCoercing = js("""{"simple":"a","one_word":"b","already_in_snake":"c","a_lot_of_words":"d","first_capitalized":"e","has_acronym_url":"BAZ","has_digit123_and_postfix":"QUX","coercion_test":"invalid"}""") + + private fun doTest(json: Json) { + val j = Json(json) { + namingStrategy = JsonNamingStrategy.SnakeCase + } + val foo = JsonNamingStrategyTest.Foo() + assertDynamicForm(foo) + assertEquals(foo, j.decodeFromDynamic(jsForm)) + } + + @Test + fun testNamingStrategyWorksWithCoercing() { + val j = Json(default) { + coerceInputValues = true + useAlternativeNames = false + namingStrategy = JsonNamingStrategy.SnakeCase + } + assertEquals(JsonNamingStrategyTest.Foo(), j.decodeFromDynamic(jsFormNeedsCoercing)) + } + + @Test + fun testJsonNamingStrategyWithAlternativeNames() = doTest(Json(default) { + useAlternativeNames = true + }) + + @Test + fun testJsonNamingStrategyWithoutAlternativeNames() = doTest(Json(default) { + useAlternativeNames = false + }) +} diff --git a/formats/json-tests/jsTest/src/kotlinx/serialization/test/CurrentPlatform.kt b/formats/json-tests/jsTest/src/kotlinx/serialization/test/CurrentPlatform.kt new file mode 100644 index 00000000..23627d17 --- /dev/null +++ b/formats/json-tests/jsTest/src/kotlinx/serialization/test/CurrentPlatform.kt @@ -0,0 +1,7 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.test + +public actual val currentPlatform: Platform = Platform.JS diff --git a/formats/json-tests/jsTest/src/kotlinx/serialization/test/JsonHelpers.kt b/formats/json-tests/jsTest/src/kotlinx/serialization/test/JsonHelpers.kt new file mode 100644 index 00000000..3f98c43d --- /dev/null +++ b/formats/json-tests/jsTest/src/kotlinx/serialization/test/JsonHelpers.kt @@ -0,0 +1,19 @@ +package kotlinx.serialization.test + +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.json.Json + +actual fun <T> Json.encodeViaStream( + serializer: SerializationStrategy<T>, + value: T +): String { + TODO("supported on JVM only") +} + +actual fun <T> Json.decodeViaStream( + serializer: DeserializationStrategy<T>, + input: String +): T { + TODO("supported on JVM only") +} diff --git a/formats/json-tests/jvmTest/resources/class_loaders/classes/example/Foo$$serializer.class b/formats/json-tests/jvmTest/resources/class_loaders/classes/example/Foo$$serializer.class Binary files differnew file mode 100644 index 00000000..3d27cb99 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/class_loaders/classes/example/Foo$$serializer.class diff --git a/formats/json-tests/jvmTest/resources/class_loaders/classes/example/Foo$Companion.class b/formats/json-tests/jvmTest/resources/class_loaders/classes/example/Foo$Companion.class Binary files differnew file mode 100644 index 00000000..9d74dadb --- /dev/null +++ b/formats/json-tests/jvmTest/resources/class_loaders/classes/example/Foo$Companion.class diff --git a/formats/json-tests/jvmTest/resources/class_loaders/classes/example/Foo.class b/formats/json-tests/jvmTest/resources/class_loaders/classes/example/Foo.class Binary files differnew file mode 100644 index 00000000..e22f4862 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/class_loaders/classes/example/Foo.class diff --git a/formats/json-tests/jvmTest/resources/corner_cases/listing.txt b/formats/json-tests/jvmTest/resources/corner_cases/listing.txt new file mode 100644 index 00000000..caa82819 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/listing.txt @@ -0,0 +1,18 @@ +number_1.0.json +number_1.000000000000000005.json +number_1000000000000000.json +number_10000000000000000999.json +number_1e-999.json +number_1e6.json +object_key_nfc_nfd.json +object_key_nfd_nfc.json +object_same_key_different_values.json +object_same_key_same_value.json +object_same_key_unclear_values.json +string_1_escaped_invalid_codepoint.json +string_1_invalid_codepoint.json +string_2_escaped_invalid_codepoints.json +string_2_invalid_codepoints.json +string_3_escaped_invalid_codepoints.json +string_3_invalid_codepoints.json +string_with_escaped_NULL.json
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/number_1.0.json b/formats/json-tests/jvmTest/resources/corner_cases/number_1.0.json new file mode 100644 index 00000000..e7a19a6e --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/number_1.0.json @@ -0,0 +1 @@ +[1.0]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/number_1.000000000000000005.json b/formats/json-tests/jvmTest/resources/corner_cases/number_1.000000000000000005.json new file mode 100644 index 00000000..c73b7cfc --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/number_1.000000000000000005.json @@ -0,0 +1 @@ +[1.000000000000000005]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/number_1000000000000000.json b/formats/json-tests/jvmTest/resources/corner_cases/number_1000000000000000.json new file mode 100644 index 00000000..cd38afa7 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/number_1000000000000000.json @@ -0,0 +1 @@ +[1000000000000000] diff --git a/formats/json-tests/jvmTest/resources/corner_cases/number_10000000000000000999.json b/formats/json-tests/jvmTest/resources/corner_cases/number_10000000000000000999.json new file mode 100644 index 00000000..946d13d3 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/number_10000000000000000999.json @@ -0,0 +1 @@ +[10000000000000000999]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/number_1e-999.json b/formats/json-tests/jvmTest/resources/corner_cases/number_1e-999.json new file mode 100644 index 00000000..c8ed222f --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/number_1e-999.json @@ -0,0 +1 @@ +[1E-999]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/number_1e6.json b/formats/json-tests/jvmTest/resources/corner_cases/number_1e6.json new file mode 100644 index 00000000..1a8b0f78 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/number_1e6.json @@ -0,0 +1 @@ +[1E6]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/object_key_nfc_nfd.json b/formats/json-tests/jvmTest/resources/corner_cases/object_key_nfc_nfd.json new file mode 100644 index 00000000..e4cbc1dc --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/object_key_nfc_nfd.json @@ -0,0 +1 @@ +{"é":"NFC","é":"NFD"}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/object_key_nfd_nfc.json b/formats/json-tests/jvmTest/resources/corner_cases/object_key_nfd_nfc.json new file mode 100644 index 00000000..b04ece18 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/object_key_nfd_nfc.json @@ -0,0 +1 @@ +{"é":"NFD","é":"NFC"}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/object_same_key_different_values.json b/formats/json-tests/jvmTest/resources/corner_cases/object_same_key_different_values.json new file mode 100644 index 00000000..0c4547df --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/object_same_key_different_values.json @@ -0,0 +1 @@ +{"a":1,"a":2}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/object_same_key_same_value.json b/formats/json-tests/jvmTest/resources/corner_cases/object_same_key_same_value.json new file mode 100644 index 00000000..e1070184 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/object_same_key_same_value.json @@ -0,0 +1 @@ +{"a":1,"a":1}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/object_same_key_unclear_values.json b/formats/json-tests/jvmTest/resources/corner_cases/object_same_key_unclear_values.json new file mode 100644 index 00000000..8a76bd4f --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/object_same_key_unclear_values.json @@ -0,0 +1 @@ +{"a":0, "a":-0} diff --git a/formats/json-tests/jvmTest/resources/corner_cases/string_1_escaped_invalid_codepoint.json b/formats/json-tests/jvmTest/resources/corner_cases/string_1_escaped_invalid_codepoint.json new file mode 100755 index 00000000..8e624731 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/string_1_escaped_invalid_codepoint.json @@ -0,0 +1 @@ +["\uD800"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/string_1_invalid_codepoint.json b/formats/json-tests/jvmTest/resources/corner_cases/string_1_invalid_codepoint.json new file mode 100755 index 00000000..916bff92 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/string_1_invalid_codepoint.json @@ -0,0 +1 @@ +[""]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/string_2_escaped_invalid_codepoints.json b/formats/json-tests/jvmTest/resources/corner_cases/string_2_escaped_invalid_codepoints.json new file mode 100755 index 00000000..93568e2c --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/string_2_escaped_invalid_codepoints.json @@ -0,0 +1 @@ +["\uD800\uD800"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/string_2_invalid_codepoints.json b/formats/json-tests/jvmTest/resources/corner_cases/string_2_invalid_codepoints.json new file mode 100755 index 00000000..043a72e8 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/string_2_invalid_codepoints.json @@ -0,0 +1 @@ +[""]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/string_3_escaped_invalid_codepoints.json b/formats/json-tests/jvmTest/resources/corner_cases/string_3_escaped_invalid_codepoints.json new file mode 100755 index 00000000..407dc657 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/string_3_escaped_invalid_codepoints.json @@ -0,0 +1 @@ +["\uD800\uD800\uD800"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/string_3_invalid_codepoints.json b/formats/json-tests/jvmTest/resources/corner_cases/string_3_invalid_codepoints.json new file mode 100755 index 00000000..2fcb0927 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/string_3_invalid_codepoints.json @@ -0,0 +1 @@ +[""]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corner_cases/string_with_escaped_NULL.json b/formats/json-tests/jvmTest/resources/corner_cases/string_with_escaped_NULL.json new file mode 100644 index 00000000..8ca2be59 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corner_cases/string_with_escaped_NULL.json @@ -0,0 +1 @@ +["A\u0000B"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/corpus.zip b/formats/json-tests/jvmTest/resources/corpus.zip Binary files differnew file mode 100644 index 00000000..a43f2952 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/corpus.zip diff --git a/formats/json-tests/jvmTest/resources/spec_cases/listing.txt b/formats/json-tests/jvmTest/resources/spec_cases/listing.txt new file mode 100644 index 00000000..c2f347cf --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/listing.txt @@ -0,0 +1,231 @@ +//n_array_a_invalid_utf8.json +//n_array_invalid_utf8.json +//n_array_just_minus.json +//n_array_star_inside.json +//n_incomplete_false.json +//n_incomplete_null.json +//n_incomplete_true.json +//n_object_bad_value.json +//n_object_key_with_single_quotes.json +//n_object_non_string_key.json +//n_object_non_string_key_but_huge_number_instead.json +//n_object_single_quote.json +//n_object_unquoted_key.json +//n_string_accentuated_char_no_quotes.json +//n_string_single_string_no_double_quotes.json +//n_string_unescaped_crtl_char.json +//n_string_unescaped_newline.json +//n_string_unescaped_tab.json +//n_structure_U+2060_word_joined.json +//n_structure_UTF8_BOM_no_data.json +//n_structure_angle_bracket_..json +//n_structure_angle_bracket_null.json +//n_structure_ascii-unicode-identifier.json +//n_structure_capitalized_True.json +//n_structure_lone-invalid-utf-8.json +//n_structure_lone-open-bracket.json +//n_structure_number_with_trailing_garbage.json +//n_structure_single_star.json +//n_structure_unicode-identifier.json +//n_structure_whitespace_U+2060_word_joiner.json +n_array_1_true_without_comma.json +n_array_colon_instead_of_comma.json +n_array_comma_after_close.json +n_array_comma_and_number.json +n_array_double_comma.json +n_array_double_extra_comma.json +n_array_extra_close.json +n_array_extra_comma.json +n_array_incomplete.json +n_array_incomplete_invalid_value.json +n_array_inner_array_no_comma.json +n_array_items_separated_by_semicolon.json +n_array_just_comma.json +n_array_missing_value.json +n_array_newlines_unclosed.json +n_array_number_and_comma.json +n_array_number_and_several_commas.json +n_array_spaces_vertical_tab_formfeed.json +n_array_unclosed.json +n_array_unclosed_trailing_comma.json +n_array_unclosed_with_new_lines.json +n_array_unclosed_with_object_inside.json +n_multidigit_number_then_00.json +n_object_bracket_key.json +n_object_comma_instead_of_colon.json +n_object_double_colon.json +n_object_emoji.json +n_object_garbage_at_end.json +n_object_lone_continuation_byte_in_key_and_trailing_comma.json +n_object_missing_colon.json +n_object_missing_key.json +n_object_missing_semicolon.json +n_object_missing_value.json +n_object_no-colon.json +n_object_repeated_null_null.json +n_object_several_trailing_commas.json +n_object_trailing_comma.json +n_object_trailing_comment.json +n_object_trailing_comment_open.json +n_object_trailing_comment_slash_open.json +n_object_trailing_comment_slash_open_incomplete.json +n_object_two_commas_in_a_row.json +n_object_unterminated-value.json +n_object_with_single_string.json +n_object_with_trailing_garbage.json +n_single_space.json +n_string_1_surrogate_then_escape.json +n_string_1_surrogate_then_escape_u.json +n_string_1_surrogate_then_escape_u1.json +n_string_1_surrogate_then_escape_u1x.json +n_string_backslash_00.json +n_string_escape_x.json +n_string_escaped_backslash_bad.json +n_string_escaped_ctrl_char_tab.json +n_string_escaped_emoji.json +n_string_incomplete_escape.json +n_string_incomplete_escaped_character.json +n_string_incomplete_surrogate.json +n_string_incomplete_surrogate_escape_invalid.json +n_string_invalid-utf-8-in-escape.json +n_string_invalid_backslash_esc.json +n_string_invalid_unicode_escape.json +n_string_invalid_utf8_after_escape.json +n_string_leading_uescaped_thinspace.json +n_string_no_quotes_with_bad_escape.json +n_string_single_doublequote.json +n_string_single_quote.json +n_string_start_escape_unclosed.json +n_string_unicode_CapitalU.json +n_string_with_trailing_garbage.json +n_structure_100000_opening_arrays.json +n_structure_array_trailing_garbage.json +n_structure_array_with_extra_array_close.json +n_structure_array_with_unclosed_string.json +n_structure_close_unopened_array.json +n_structure_comma_instead_of_closing_brace.json +n_structure_double_array.json +n_structure_end_array.json +n_structure_incomplete_UTF8_BOM.json +n_structure_no_data.json +n_structure_null-byte-outside-string.json +n_structure_object_followed_by_closing_object.json +n_structure_object_unclosed_no_value.json +n_structure_object_with_comment.json +n_structure_object_with_trailing_garbage.json +n_structure_open_array_apostrophe.json +n_structure_open_array_comma.json +n_structure_open_array_object.json +n_structure_open_array_open_object.json +n_structure_open_array_open_string.json +n_structure_open_array_string.json +n_structure_open_object.json +n_structure_open_object_close_array.json +n_structure_open_object_comma.json +n_structure_open_object_open_array.json +n_structure_open_object_open_string.json +n_structure_open_object_string_with_apostrophes.json +n_structure_open_open.json +n_structure_trailing_#.json +n_structure_uescaped_LF_before_string.json +n_structure_unclosed_array.json +n_structure_unclosed_array_partial_null.json +n_structure_unclosed_array_unfinished_false.json +n_structure_unclosed_array_unfinished_true.json +n_structure_unclosed_object.json +n_structure_whitespace_formfeed.json +y_array_arraysWithSpaces.json +y_array_empty-string.json +y_array_empty.json +y_array_ending_with_newline.json +y_array_false.json +y_array_heterogeneous.json +y_array_null.json +y_array_with_1_and_newline.json +y_array_with_leading_space.json +y_array_with_several_null.json +y_array_with_trailing_space.json +y_number.json +y_number_0e+1.json +y_number_0e1.json +y_number_after_space.json +y_number_double_close_to_zero.json +y_number_int_with_exp.json +y_number_minus_zero.json +y_number_negative_int.json +y_number_negative_one.json +y_number_negative_zero.json +y_number_real_capital_e.json +y_number_real_capital_e_neg_exp.json +y_number_real_capital_e_pos_exp.json +y_number_real_exponent.json +y_number_real_fraction_exponent.json +y_number_real_neg_exp.json +y_number_real_pos_exponent.json +y_number_simple_int.json +y_number_simple_real.json +y_object.json +y_object_basic.json +y_object_duplicated_key.json +y_object_duplicated_key_and_value.json +y_object_empty.json +y_object_empty_key.json +y_object_escaped_null_in_key.json +y_object_extreme_numbers.json +y_object_long_strings.json +y_object_simple.json +y_object_string_unicode.json +y_object_with_newlines.json +y_string_1_2_3_bytes_UTF-8_sequences.json +y_string_accepted_surrogate_pair.json +y_string_accepted_surrogate_pairs.json +y_string_allowed_escapes.json +y_string_backslash_and_u_escaped_zero.json +y_string_backslash_doublequotes.json +y_string_comments.json +y_string_double_escape_a.json +y_string_double_escape_n.json +y_string_escaped_control_character.json +y_string_escaped_noncharacter.json +y_string_in_array.json +y_string_in_array_with_leading_space.json +y_string_last_surrogates_1_and_2.json +y_string_nbsp_uescaped.json +y_string_nonCharacterInUTF-8_U+10FFFF.json +y_string_nonCharacterInUTF-8_U+FFFF.json +y_string_null_escape.json +y_string_one-byte-utf-8.json +y_string_pi.json +y_string_reservedCharacterInUTF-8_U+1BFFF.json +y_string_simple_ascii.json +y_string_space.json +y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json +y_string_three-byte-utf-8.json +y_string_two-byte-utf-8.json +y_string_u+2028_line_sep.json +y_string_u+2029_par_sep.json +y_string_uEscape.json +y_string_uescaped_newline.json +y_string_unescaped_char_delete.json +y_string_unicode.json +y_string_unicodeEscapedBackslash.json +y_string_unicode_2.json +y_string_unicode_U+10FFFE_nonchar.json +y_string_unicode_U+1FFFE_nonchar.json +y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json +y_string_unicode_U+2064_invisible_plus.json +y_string_unicode_U+FDD0_nonchar.json +y_string_unicode_U+FFFE_nonchar.json +y_string_unicode_escaped_double_quote.json +y_string_utf8.json +y_string_with_del_character.json +y_structure_lonely_false.json +y_structure_lonely_int.json +y_structure_lonely_negative_real.json +y_structure_lonely_null.json +y_structure_lonely_string.json +y_structure_lonely_true.json +y_structure_string_empty.json +y_structure_trailing_newline.json +y_structure_true_in_array.json +y_structure_whitespace_array.json
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_1_true_without_comma.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_1_true_without_comma.json new file mode 100644 index 00000000..c14e3f6b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_1_true_without_comma.json @@ -0,0 +1 @@ +[1 true]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_a_invalid_utf8.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_a_invalid_utf8.json new file mode 100644 index 00000000..38a86e2e --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_a_invalid_utf8.json @@ -0,0 +1 @@ +[a]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_colon_instead_of_comma.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_colon_instead_of_comma.json new file mode 100644 index 00000000..0d02ad44 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_colon_instead_of_comma.json @@ -0,0 +1 @@ +["": 1]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_comma_after_close.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_comma_after_close.json new file mode 100644 index 00000000..2ccba8d9 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_comma_after_close.json @@ -0,0 +1 @@ +[""],
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_comma_and_number.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_comma_and_number.json new file mode 100755 index 00000000..d2c84e37 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_comma_and_number.json @@ -0,0 +1 @@ +[,1]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_double_comma.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_double_comma.json new file mode 100755 index 00000000..0431712b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_double_comma.json @@ -0,0 +1 @@ +[1,,2]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_double_extra_comma.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_double_extra_comma.json new file mode 100644 index 00000000..3f01d312 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_double_extra_comma.json @@ -0,0 +1 @@ +["x",,]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_extra_close.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_extra_close.json new file mode 100644 index 00000000..c12f9fae --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_extra_close.json @@ -0,0 +1 @@ +["x"]]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_extra_comma.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_extra_comma.json new file mode 100644 index 00000000..5f8ce18e --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_extra_comma.json @@ -0,0 +1 @@ +["",]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_incomplete.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_incomplete.json new file mode 100644 index 00000000..cc65b0b5 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_incomplete.json @@ -0,0 +1 @@ +["x"
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_incomplete_invalid_value.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_incomplete_invalid_value.json new file mode 100644 index 00000000..c21a8f6c --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_incomplete_invalid_value.json @@ -0,0 +1 @@ +[x
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_inner_array_no_comma.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_inner_array_no_comma.json new file mode 100644 index 00000000..c70b7164 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_inner_array_no_comma.json @@ -0,0 +1 @@ +[3[4]]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_invalid_utf8.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_invalid_utf8.json new file mode 100644 index 00000000..6099d344 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_invalid_utf8.json @@ -0,0 +1 @@ +[]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_items_separated_by_semicolon.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_items_separated_by_semicolon.json new file mode 100755 index 00000000..d4bd7314 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_items_separated_by_semicolon.json @@ -0,0 +1 @@ +[1:2]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_just_comma.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_just_comma.json new file mode 100755 index 00000000..9d7077c6 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_just_comma.json @@ -0,0 +1 @@ +[,]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_just_minus.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_just_minus.json new file mode 100755 index 00000000..29501c6c --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_just_minus.json @@ -0,0 +1 @@ +[-]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_missing_value.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_missing_value.json new file mode 100644 index 00000000..3a6ba86f --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_missing_value.json @@ -0,0 +1 @@ +[ , ""]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_newlines_unclosed.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_newlines_unclosed.json new file mode 100644 index 00000000..64668006 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_newlines_unclosed.json @@ -0,0 +1,3 @@ +["a", +4 +,1,
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_number_and_comma.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_number_and_comma.json new file mode 100755 index 00000000..13f6f1d1 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_number_and_comma.json @@ -0,0 +1 @@ +[1,]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_number_and_several_commas.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_number_and_several_commas.json new file mode 100755 index 00000000..0ac408cb --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_number_and_several_commas.json @@ -0,0 +1 @@ +[1,,]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_spaces_vertical_tab_formfeed.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_spaces_vertical_tab_formfeed.json new file mode 100755 index 00000000..6cd7cf58 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_spaces_vertical_tab_formfeed.json @@ -0,0 +1 @@ +["a"\f]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_star_inside.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_star_inside.json new file mode 100755 index 00000000..5a519464 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_star_inside.json @@ -0,0 +1 @@ +[*]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_unclosed.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_unclosed.json new file mode 100644 index 00000000..06073305 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_unclosed.json @@ -0,0 +1 @@ +[""
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_unclosed_trailing_comma.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_unclosed_trailing_comma.json new file mode 100644 index 00000000..6604698f --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_unclosed_trailing_comma.json @@ -0,0 +1 @@ +[1,
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_unclosed_with_new_lines.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_unclosed_with_new_lines.json new file mode 100644 index 00000000..4f61de3f --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_unclosed_with_new_lines.json @@ -0,0 +1,3 @@ +[1, +1 +,1
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_array_unclosed_with_object_inside.json b/formats/json-tests/jvmTest/resources/spec_cases/n_array_unclosed_with_object_inside.json new file mode 100644 index 00000000..043a87e2 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_array_unclosed_with_object_inside.json @@ -0,0 +1 @@ +[{}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_incomplete_false.json b/formats/json-tests/jvmTest/resources/spec_cases/n_incomplete_false.json new file mode 100644 index 00000000..eb18c6a1 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_incomplete_false.json @@ -0,0 +1 @@ +[fals]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_incomplete_null.json b/formats/json-tests/jvmTest/resources/spec_cases/n_incomplete_null.json new file mode 100644 index 00000000..c18ef538 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_incomplete_null.json @@ -0,0 +1 @@ +[nul]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_incomplete_true.json b/formats/json-tests/jvmTest/resources/spec_cases/n_incomplete_true.json new file mode 100644 index 00000000..f451ac6d --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_incomplete_true.json @@ -0,0 +1 @@ +[tru]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_multidigit_number_then_00.json b/formats/json-tests/jvmTest/resources/spec_cases/n_multidigit_number_then_00.json Binary files differnew file mode 100644 index 00000000..c22507b8 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_multidigit_number_then_00.json diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_bad_value.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_bad_value.json new file mode 100644 index 00000000..a03a8c03 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_bad_value.json @@ -0,0 +1 @@ +["x", truth]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_bracket_key.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_bracket_key.json new file mode 100644 index 00000000..cc443b48 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_bracket_key.json @@ -0,0 +1 @@ +{[: "x"} diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_comma_instead_of_colon.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_comma_instead_of_colon.json new file mode 100644 index 00000000..8d563770 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_comma_instead_of_colon.json @@ -0,0 +1 @@ +{"x", null}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_double_colon.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_double_colon.json new file mode 100644 index 00000000..80e8c7b8 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_double_colon.json @@ -0,0 +1 @@ +{"x"::"b"}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_emoji.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_emoji.json new file mode 100644 index 00000000..cb4078ea --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_emoji.json @@ -0,0 +1 @@ +{🇨🇭}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_garbage_at_end.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_garbage_at_end.json new file mode 100644 index 00000000..80c42cba --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_garbage_at_end.json @@ -0,0 +1 @@ +{"a":"a" 123}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_key_with_single_quotes.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_key_with_single_quotes.json new file mode 100755 index 00000000..77c32759 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_key_with_single_quotes.json @@ -0,0 +1 @@ +{key: 'value'}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_lone_continuation_byte_in_key_and_trailing_comma.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_lone_continuation_byte_in_key_and_trailing_comma.json new file mode 100644 index 00000000..aa2cb637 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_lone_continuation_byte_in_key_and_trailing_comma.json @@ -0,0 +1 @@ +{"":"0",}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_missing_colon.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_missing_colon.json new file mode 100644 index 00000000..b98eff62 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_missing_colon.json @@ -0,0 +1 @@ +{"a" b}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_missing_key.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_missing_key.json new file mode 100755 index 00000000..b4fb0f52 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_missing_key.json @@ -0,0 +1 @@ +{:"b"}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_missing_semicolon.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_missing_semicolon.json new file mode 100755 index 00000000..e3451384 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_missing_semicolon.json @@ -0,0 +1 @@ +{"a" "b"}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_missing_value.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_missing_value.json new file mode 100644 index 00000000..3ef538a6 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_missing_value.json @@ -0,0 +1 @@ +{"a":
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_no-colon.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_no-colon.json new file mode 100644 index 00000000..f3797b35 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_no-colon.json @@ -0,0 +1 @@ +{"a"
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_non_string_key.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_non_string_key.json new file mode 100755 index 00000000..b9945b34 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_non_string_key.json @@ -0,0 +1 @@ +{1:1}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_non_string_key_but_huge_number_instead.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_non_string_key_but_huge_number_instead.json new file mode 100755 index 00000000..b37fa86c --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_non_string_key_but_huge_number_instead.json @@ -0,0 +1 @@ +{9999E9999:1}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_repeated_null_null.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_repeated_null_null.json new file mode 100755 index 00000000..f7d2959d --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_repeated_null_null.json @@ -0,0 +1 @@ +{null:null,null:null}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_several_trailing_commas.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_several_trailing_commas.json new file mode 100755 index 00000000..3c9afe8d --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_several_trailing_commas.json @@ -0,0 +1 @@ +{"id":0,,,,,}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_single_quote.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_single_quote.json new file mode 100644 index 00000000..e5cdf976 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_single_quote.json @@ -0,0 +1 @@ +{'a':0}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_trailing_comma.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_trailing_comma.json new file mode 100755 index 00000000..a4b02509 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_trailing_comma.json @@ -0,0 +1 @@ +{"id":0,}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_trailing_comment.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_trailing_comment.json new file mode 100644 index 00000000..a372c655 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_trailing_comment.json @@ -0,0 +1 @@ +{"a":"b"}/**/
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_trailing_comment_open.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_trailing_comment_open.json new file mode 100644 index 00000000..d557f41c --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_trailing_comment_open.json @@ -0,0 +1 @@ +{"a":"b"}/**//
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_trailing_comment_slash_open.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_trailing_comment_slash_open.json new file mode 100644 index 00000000..e335136c --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_trailing_comment_slash_open.json @@ -0,0 +1 @@ +{"a":"b"}//
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_trailing_comment_slash_open_incomplete.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_trailing_comment_slash_open_incomplete.json new file mode 100644 index 00000000..d892e49f --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_trailing_comment_slash_open_incomplete.json @@ -0,0 +1 @@ +{"a":"b"}/
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_two_commas_in_a_row.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_two_commas_in_a_row.json new file mode 100755 index 00000000..7c639ae6 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_two_commas_in_a_row.json @@ -0,0 +1 @@ +{"a":"b",,"c":"d"}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_unquoted_key.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_unquoted_key.json new file mode 100644 index 00000000..8ba13729 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_unquoted_key.json @@ -0,0 +1 @@ +{a: "b"}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_unterminated-value.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_unterminated-value.json new file mode 100644 index 00000000..7fe699a6 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_unterminated-value.json @@ -0,0 +1 @@ +{"a":"a
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_with_single_string.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_with_single_string.json new file mode 100644 index 00000000..d63f7fbb --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_with_single_string.json @@ -0,0 +1 @@ +{ "foo" : "bar", "a" }
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_object_with_trailing_garbage.json b/formats/json-tests/jvmTest/resources/spec_cases/n_object_with_trailing_garbage.json new file mode 100644 index 00000000..787c8f0a --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_object_with_trailing_garbage.json @@ -0,0 +1 @@ +{"a":"b"}#
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_single_space.json b/formats/json-tests/jvmTest/resources/spec_cases/n_single_space.json new file mode 100755 index 00000000..0519ecba --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_single_space.json @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape.json new file mode 100644 index 00000000..acec66d8 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape.json @@ -0,0 +1 @@ +["\uD800\"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u.json new file mode 100644 index 00000000..e834b05e --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u.json @@ -0,0 +1 @@ +["\uD800\u"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u1.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u1.json new file mode 100644 index 00000000..a04cd348 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u1.json @@ -0,0 +1 @@ +["\uD800\u1"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u1x.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u1x.json new file mode 100644 index 00000000..bfbd2340 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_1_surrogate_then_escape_u1x.json @@ -0,0 +1 @@ +["\uD800\u1x"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_accentuated_char_no_quotes.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_accentuated_char_no_quotes.json new file mode 100644 index 00000000..fd689569 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_accentuated_char_no_quotes.json @@ -0,0 +1 @@ +[é]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_backslash_00.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_backslash_00.json Binary files differnew file mode 100644 index 00000000..b5bf267b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_backslash_00.json diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_escape_x.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_escape_x.json new file mode 100644 index 00000000..fae29193 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_escape_x.json @@ -0,0 +1 @@ +["\x00"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_escaped_backslash_bad.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_escaped_backslash_bad.json new file mode 100755 index 00000000..016fcb47 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_escaped_backslash_bad.json @@ -0,0 +1 @@ +["\\\"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_escaped_ctrl_char_tab.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_escaped_ctrl_char_tab.json new file mode 100644 index 00000000..f35ea382 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_escaped_ctrl_char_tab.json @@ -0,0 +1 @@ +["\ "]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_escaped_emoji.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_escaped_emoji.json new file mode 100644 index 00000000..a2777542 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_escaped_emoji.json @@ -0,0 +1 @@ +["\🌀"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_incomplete_escape.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_incomplete_escape.json new file mode 100755 index 00000000..3415c33c --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_incomplete_escape.json @@ -0,0 +1 @@ +["\"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_incomplete_escaped_character.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_incomplete_escaped_character.json new file mode 100755 index 00000000..0f2197ea --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_incomplete_escaped_character.json @@ -0,0 +1 @@ +["\u00A"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_incomplete_surrogate.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_incomplete_surrogate.json new file mode 100755 index 00000000..75504a65 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_incomplete_surrogate.json @@ -0,0 +1 @@ +["\uD834\uDd"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_incomplete_surrogate_escape_invalid.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_incomplete_surrogate_escape_invalid.json new file mode 100755 index 00000000..bd965606 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_incomplete_surrogate_escape_invalid.json @@ -0,0 +1 @@ +["\uD800\uD800\x"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_invalid-utf-8-in-escape.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_invalid-utf-8-in-escape.json new file mode 100644 index 00000000..0c430064 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_invalid-utf-8-in-escape.json @@ -0,0 +1 @@ +["\u"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_invalid_backslash_esc.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_invalid_backslash_esc.json new file mode 100755 index 00000000..d1eb6092 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_invalid_backslash_esc.json @@ -0,0 +1 @@ +["\a"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_invalid_unicode_escape.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_invalid_unicode_escape.json new file mode 100644 index 00000000..7608cb6b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_invalid_unicode_escape.json @@ -0,0 +1 @@ +["\uqqqq"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_invalid_utf8_after_escape.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_invalid_utf8_after_escape.json new file mode 100644 index 00000000..2f757a25 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_invalid_utf8_after_escape.json @@ -0,0 +1 @@ +["\"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_leading_uescaped_thinspace.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_leading_uescaped_thinspace.json new file mode 100755 index 00000000..7b297c63 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_leading_uescaped_thinspace.json @@ -0,0 +1 @@ +[\u0020"asd"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_no_quotes_with_bad_escape.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_no_quotes_with_bad_escape.json new file mode 100644 index 00000000..01bc70ab --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_no_quotes_with_bad_escape.json @@ -0,0 +1 @@ +[\n]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_single_doublequote.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_single_doublequote.json new file mode 100755 index 00000000..9d68933c --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_single_doublequote.json @@ -0,0 +1 @@ +"
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_single_quote.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_single_quote.json new file mode 100644 index 00000000..caff239b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_single_quote.json @@ -0,0 +1 @@ +['single quote']
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_single_string_no_double_quotes.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_single_string_no_double_quotes.json new file mode 100755 index 00000000..f2ba8f84 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_single_string_no_double_quotes.json @@ -0,0 +1 @@ +abc
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_start_escape_unclosed.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_start_escape_unclosed.json new file mode 100644 index 00000000..db62a46f --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_start_escape_unclosed.json @@ -0,0 +1 @@ +["\
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_unescaped_crtl_char.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_unescaped_crtl_char.json Binary files differnew file mode 100755 index 00000000..9f213480 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_unescaped_crtl_char.json diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_unescaped_newline.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_unescaped_newline.json new file mode 100644 index 00000000..700d3608 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_unescaped_newline.json @@ -0,0 +1,2 @@ +["new +line"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_unescaped_tab.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_unescaped_tab.json new file mode 100644 index 00000000..160264a2 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_unescaped_tab.json @@ -0,0 +1 @@ +[" "]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_unicode_CapitalU.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_unicode_CapitalU.json new file mode 100644 index 00000000..17332bb1 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_unicode_CapitalU.json @@ -0,0 +1 @@ +"\UA66D"
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_string_with_trailing_garbage.json b/formats/json-tests/jvmTest/resources/spec_cases/n_string_with_trailing_garbage.json new file mode 100644 index 00000000..efe3bd27 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_string_with_trailing_garbage.json @@ -0,0 +1 @@ +""x
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_100000_opening_arrays.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_100000_opening_arrays.json new file mode 100644 index 00000000..a4823eec --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_100000_opening_arrays.json @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_U+2060_word_joined.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_U+2060_word_joined.json new file mode 100644 index 00000000..81156a69 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_U+2060_word_joined.json @@ -0,0 +1 @@ +[]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_UTF8_BOM_no_data.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_UTF8_BOM_no_data.json new file mode 100755 index 00000000..5f282702 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_UTF8_BOM_no_data.json @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_angle_bracket_..json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_angle_bracket_..json new file mode 100755 index 00000000..a56fef0b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_angle_bracket_..json @@ -0,0 +1 @@ +<.>
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_angle_bracket_null.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_angle_bracket_null.json new file mode 100755 index 00000000..617f2625 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_angle_bracket_null.json @@ -0,0 +1 @@ +[<null>]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_array_trailing_garbage.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_array_trailing_garbage.json new file mode 100644 index 00000000..5a745e6f --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_array_trailing_garbage.json @@ -0,0 +1 @@ +[1]x
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_array_with_extra_array_close.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_array_with_extra_array_close.json new file mode 100755 index 00000000..6cfb1398 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_array_with_extra_array_close.json @@ -0,0 +1 @@ +[1]]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_array_with_unclosed_string.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_array_with_unclosed_string.json new file mode 100755 index 00000000..ba6b1788 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_array_with_unclosed_string.json @@ -0,0 +1 @@ +["asd]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_ascii-unicode-identifier.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_ascii-unicode-identifier.json new file mode 100644 index 00000000..ef2ab62f --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_ascii-unicode-identifier.json @@ -0,0 +1 @@ +aå
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_capitalized_True.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_capitalized_True.json new file mode 100755 index 00000000..7cd88469 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_capitalized_True.json @@ -0,0 +1 @@ +[True]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_close_unopened_array.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_close_unopened_array.json new file mode 100755 index 00000000..d2af0c64 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_close_unopened_array.json @@ -0,0 +1 @@ +1]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_comma_instead_of_closing_brace.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_comma_instead_of_closing_brace.json new file mode 100644 index 00000000..ac61b820 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_comma_instead_of_closing_brace.json @@ -0,0 +1 @@ +{"x": true,
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_double_array.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_double_array.json new file mode 100755 index 00000000..058d1626 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_double_array.json @@ -0,0 +1 @@ +[][]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_end_array.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_end_array.json new file mode 100644 index 00000000..54caf60b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_end_array.json @@ -0,0 +1 @@ +]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_incomplete_UTF8_BOM.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_incomplete_UTF8_BOM.json new file mode 100755 index 00000000..bfcdd514 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_incomplete_UTF8_BOM.json @@ -0,0 +1 @@ +{}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_lone-invalid-utf-8.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_lone-invalid-utf-8.json new file mode 100644 index 00000000..8b1296ca --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_lone-invalid-utf-8.json @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_lone-open-bracket.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_lone-open-bracket.json new file mode 100644 index 00000000..8e2f0bef --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_lone-open-bracket.json @@ -0,0 +1 @@ +[
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_no_data.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_no_data.json new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_no_data.json diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_null-byte-outside-string.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_null-byte-outside-string.json Binary files differnew file mode 100644 index 00000000..326db144 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_null-byte-outside-string.json diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_number_with_trailing_garbage.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_number_with_trailing_garbage.json new file mode 100644 index 00000000..0746539d --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_number_with_trailing_garbage.json @@ -0,0 +1 @@ +2@
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_object_followed_by_closing_object.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_object_followed_by_closing_object.json new file mode 100644 index 00000000..aa9ebaec --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_object_followed_by_closing_object.json @@ -0,0 +1 @@ +{}}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_object_unclosed_no_value.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_object_unclosed_no_value.json new file mode 100644 index 00000000..17d04514 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_object_unclosed_no_value.json @@ -0,0 +1 @@ +{"":
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_object_with_comment.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_object_with_comment.json new file mode 100644 index 00000000..ed1b569b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_object_with_comment.json @@ -0,0 +1 @@ +{"a":/*comment*/"b"}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_object_with_trailing_garbage.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_object_with_trailing_garbage.json new file mode 100644 index 00000000..9ca2336d --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_object_with_trailing_garbage.json @@ -0,0 +1 @@ +{"a": true} "x"
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_apostrophe.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_apostrophe.json new file mode 100644 index 00000000..8bebe3af --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_apostrophe.json @@ -0,0 +1 @@ +['
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_comma.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_comma.json new file mode 100644 index 00000000..6295fdc3 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_comma.json @@ -0,0 +1 @@ +[,
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_object.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_object.json new file mode 100644 index 00000000..e870445b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_object.json @@ -0,0 +1 @@ +[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"": diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_open_object.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_open_object.json new file mode 100644 index 00000000..7a63c8c5 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_open_object.json @@ -0,0 +1 @@ +[{
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_open_string.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_open_string.json new file mode 100644 index 00000000..9822a6ba --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_open_string.json @@ -0,0 +1 @@ +["a
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_string.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_string.json new file mode 100644 index 00000000..42a61936 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_array_string.json @@ -0,0 +1 @@ +["a"
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object.json new file mode 100644 index 00000000..81750b96 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object.json @@ -0,0 +1 @@ +{
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object_close_array.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object_close_array.json new file mode 100755 index 00000000..eebc700a --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object_close_array.json @@ -0,0 +1 @@ +{]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object_comma.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object_comma.json new file mode 100644 index 00000000..47bc9106 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object_comma.json @@ -0,0 +1 @@ +{,
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object_open_array.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object_open_array.json new file mode 100644 index 00000000..381ede5d --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object_open_array.json @@ -0,0 +1 @@ +{[
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object_open_string.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object_open_string.json new file mode 100644 index 00000000..328c30cd --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object_open_string.json @@ -0,0 +1 @@ +{"a
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object_string_with_apostrophes.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object_string_with_apostrophes.json new file mode 100644 index 00000000..9dba1709 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_object_string_with_apostrophes.json @@ -0,0 +1 @@ +{'a'
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_open.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_open.json new file mode 100644 index 00000000..841fd5f8 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_open_open.json @@ -0,0 +1 @@ +["\{["\{["\{["\{
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_single_star.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_single_star.json new file mode 100755 index 00000000..f59ec20a --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_single_star.json @@ -0,0 +1 @@ +*
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_trailing_#.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_trailing_#.json new file mode 100644 index 00000000..89861108 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_trailing_#.json @@ -0,0 +1 @@ +{"a":"b"}#{}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_uescaped_LF_before_string.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_uescaped_LF_before_string.json new file mode 100755 index 00000000..df2f0f24 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_uescaped_LF_before_string.json @@ -0,0 +1 @@ +[\u000A""]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unclosed_array.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unclosed_array.json new file mode 100755 index 00000000..11209515 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unclosed_array.json @@ -0,0 +1 @@ +[1
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unclosed_array_partial_null.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unclosed_array_partial_null.json new file mode 100644 index 00000000..0d591762 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unclosed_array_partial_null.json @@ -0,0 +1 @@ +[ false, nul
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unclosed_array_unfinished_false.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unclosed_array_unfinished_false.json new file mode 100644 index 00000000..a2ff8504 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unclosed_array_unfinished_false.json @@ -0,0 +1 @@ +[ true, fals
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unclosed_array_unfinished_true.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unclosed_array_unfinished_true.json new file mode 100644 index 00000000..3149e8f5 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unclosed_array_unfinished_true.json @@ -0,0 +1 @@ +[ false, tru
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unclosed_object.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unclosed_object.json new file mode 100755 index 00000000..694d69db --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unclosed_object.json @@ -0,0 +1 @@ +{"asd":"asd"
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unicode-identifier.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unicode-identifier.json new file mode 100644 index 00000000..7284aea3 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_unicode-identifier.json @@ -0,0 +1 @@ +å
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_whitespace_U+2060_word_joiner.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_whitespace_U+2060_word_joiner.json new file mode 100755 index 00000000..81156a69 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_whitespace_U+2060_word_joiner.json @@ -0,0 +1 @@ +[]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/n_structure_whitespace_formfeed.json b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_whitespace_formfeed.json new file mode 100755 index 00000000..a9ea535d --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/n_structure_whitespace_formfeed.json @@ -0,0 +1 @@ +[]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_array_arraysWithSpaces.json b/formats/json-tests/jvmTest/resources/spec_cases/y_array_arraysWithSpaces.json new file mode 100755 index 00000000..58229079 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_array_arraysWithSpaces.json @@ -0,0 +1 @@ +[[] ]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_array_empty-string.json b/formats/json-tests/jvmTest/resources/spec_cases/y_array_empty-string.json new file mode 100644 index 00000000..93b6be2b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_array_empty-string.json @@ -0,0 +1 @@ +[""]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_array_empty.json b/formats/json-tests/jvmTest/resources/spec_cases/y_array_empty.json new file mode 100755 index 00000000..0637a088 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_array_empty.json @@ -0,0 +1 @@ +[]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_array_ending_with_newline.json b/formats/json-tests/jvmTest/resources/spec_cases/y_array_ending_with_newline.json new file mode 100755 index 00000000..eac5f7b4 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_array_ending_with_newline.json @@ -0,0 +1 @@ +["a"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_array_false.json b/formats/json-tests/jvmTest/resources/spec_cases/y_array_false.json new file mode 100644 index 00000000..67b2f076 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_array_false.json @@ -0,0 +1 @@ +[false]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_array_heterogeneous.json b/formats/json-tests/jvmTest/resources/spec_cases/y_array_heterogeneous.json new file mode 100755 index 00000000..d3c1e264 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_array_heterogeneous.json @@ -0,0 +1 @@ +[null, 1, "1", {}]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_array_null.json b/formats/json-tests/jvmTest/resources/spec_cases/y_array_null.json new file mode 100644 index 00000000..500db4a8 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_array_null.json @@ -0,0 +1 @@ +[null]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_array_with_1_and_newline.json b/formats/json-tests/jvmTest/resources/spec_cases/y_array_with_1_and_newline.json new file mode 100644 index 00000000..99482550 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_array_with_1_and_newline.json @@ -0,0 +1,2 @@ +[1 +]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_array_with_leading_space.json b/formats/json-tests/jvmTest/resources/spec_cases/y_array_with_leading_space.json new file mode 100755 index 00000000..18bfe642 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_array_with_leading_space.json @@ -0,0 +1 @@ + [1]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_array_with_several_null.json b/formats/json-tests/jvmTest/resources/spec_cases/y_array_with_several_null.json new file mode 100755 index 00000000..99f6c5d1 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_array_with_several_null.json @@ -0,0 +1 @@ +[1,null,null,null,2]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_array_with_trailing_space.json b/formats/json-tests/jvmTest/resources/spec_cases/y_array_with_trailing_space.json new file mode 100755 index 00000000..de9e7a94 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_array_with_trailing_space.json @@ -0,0 +1 @@ +[2]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number.json new file mode 100644 index 00000000..e5f5cc33 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number.json @@ -0,0 +1 @@ +[123e65]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_0e+1.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_0e+1.json new file mode 100755 index 00000000..d1d39670 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_0e+1.json @@ -0,0 +1 @@ +[0e+1]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_0e1.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_0e1.json new file mode 100755 index 00000000..3283a793 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_0e1.json @@ -0,0 +1 @@ +[0e1]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_after_space.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_after_space.json new file mode 100644 index 00000000..623570d9 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_after_space.json @@ -0,0 +1 @@ +[ 4]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_double_close_to_zero.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_double_close_to_zero.json new file mode 100755 index 00000000..96555ff7 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_double_close_to_zero.json @@ -0,0 +1 @@ +[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_int_with_exp.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_int_with_exp.json new file mode 100755 index 00000000..a4ca9e75 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_int_with_exp.json @@ -0,0 +1 @@ +[20e1]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_minus_zero.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_minus_zero.json new file mode 100755 index 00000000..37af1312 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_minus_zero.json @@ -0,0 +1 @@ +[-0]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_negative_int.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_negative_int.json new file mode 100644 index 00000000..8e30f8bd --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_negative_int.json @@ -0,0 +1 @@ +[-123]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_negative_one.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_negative_one.json new file mode 100644 index 00000000..99d21a2a --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_negative_one.json @@ -0,0 +1 @@ +[-1]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_negative_zero.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_negative_zero.json new file mode 100644 index 00000000..37af1312 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_negative_zero.json @@ -0,0 +1 @@ +[-0]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_capital_e.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_capital_e.json new file mode 100644 index 00000000..6edbdfcb --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_capital_e.json @@ -0,0 +1 @@ +[1E22]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_capital_e_neg_exp.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_capital_e_neg_exp.json new file mode 100644 index 00000000..0a01bd3e --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_capital_e_neg_exp.json @@ -0,0 +1 @@ +[1E-2]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_capital_e_pos_exp.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_capital_e_pos_exp.json new file mode 100644 index 00000000..5a8fc097 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_capital_e_pos_exp.json @@ -0,0 +1 @@ +[1E+2]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_exponent.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_exponent.json new file mode 100644 index 00000000..da2522d6 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_exponent.json @@ -0,0 +1 @@ +[123e45]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_fraction_exponent.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_fraction_exponent.json new file mode 100644 index 00000000..3944a7a4 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_fraction_exponent.json @@ -0,0 +1 @@ +[123.456e78]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_neg_exp.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_neg_exp.json new file mode 100644 index 00000000..ca40d3c2 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_neg_exp.json @@ -0,0 +1 @@ +[1e-2]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_pos_exponent.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_pos_exponent.json new file mode 100644 index 00000000..343601d5 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_real_pos_exponent.json @@ -0,0 +1 @@ +[1e+2]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_simple_int.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_simple_int.json new file mode 100644 index 00000000..e47f69af --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_simple_int.json @@ -0,0 +1 @@ +[123]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_number_simple_real.json b/formats/json-tests/jvmTest/resources/spec_cases/y_number_simple_real.json new file mode 100644 index 00000000..b02878e5 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_number_simple_real.json @@ -0,0 +1 @@ +[123.456789]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_object.json b/formats/json-tests/jvmTest/resources/spec_cases/y_object.json new file mode 100755 index 00000000..78262eda --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_object.json @@ -0,0 +1 @@ +{"asd":"sdf", "dfg":"fgh"}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_object_basic.json b/formats/json-tests/jvmTest/resources/spec_cases/y_object_basic.json new file mode 100755 index 00000000..646bbe7b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_object_basic.json @@ -0,0 +1 @@ +{"asd":"sdf"}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_object_duplicated_key.json b/formats/json-tests/jvmTest/resources/spec_cases/y_object_duplicated_key.json new file mode 100755 index 00000000..bbc2e1ce --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_object_duplicated_key.json @@ -0,0 +1 @@ +{"a":"b","a":"c"}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_object_duplicated_key_and_value.json b/formats/json-tests/jvmTest/resources/spec_cases/y_object_duplicated_key_and_value.json new file mode 100755 index 00000000..211581c2 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_object_duplicated_key_and_value.json @@ -0,0 +1 @@ +{"a":"b","a":"b"}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_object_empty.json b/formats/json-tests/jvmTest/resources/spec_cases/y_object_empty.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_object_empty.json @@ -0,0 +1 @@ +{}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_object_empty_key.json b/formats/json-tests/jvmTest/resources/spec_cases/y_object_empty_key.json new file mode 100755 index 00000000..c0013d3b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_object_empty_key.json @@ -0,0 +1 @@ +{"":0}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_object_escaped_null_in_key.json b/formats/json-tests/jvmTest/resources/spec_cases/y_object_escaped_null_in_key.json new file mode 100644 index 00000000..593f0f67 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_object_escaped_null_in_key.json @@ -0,0 +1 @@ +{"foo\u0000bar": 42}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_object_extreme_numbers.json b/formats/json-tests/jvmTest/resources/spec_cases/y_object_extreme_numbers.json new file mode 100644 index 00000000..a0d3531c --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_object_extreme_numbers.json @@ -0,0 +1 @@ +{ "min": -1.0e+28, "max": 1.0e+28 }
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_object_long_strings.json b/formats/json-tests/jvmTest/resources/spec_cases/y_object_long_strings.json new file mode 100644 index 00000000..bdc4a087 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_object_long_strings.json @@ -0,0 +1 @@ +{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_object_simple.json b/formats/json-tests/jvmTest/resources/spec_cases/y_object_simple.json new file mode 100644 index 00000000..dacac917 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_object_simple.json @@ -0,0 +1 @@ +{"a":[]}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_object_string_unicode.json b/formats/json-tests/jvmTest/resources/spec_cases/y_object_string_unicode.json new file mode 100644 index 00000000..8effdb29 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_object_string_unicode.json @@ -0,0 +1 @@ +{"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" }
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_object_with_newlines.json b/formats/json-tests/jvmTest/resources/spec_cases/y_object_with_newlines.json new file mode 100644 index 00000000..246ec6b3 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_object_with_newlines.json @@ -0,0 +1,3 @@ +{ +"a": "b" +}
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_1_2_3_bytes_UTF-8_sequences.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_1_2_3_bytes_UTF-8_sequences.json new file mode 100755 index 00000000..9967ddeb --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_1_2_3_bytes_UTF-8_sequences.json @@ -0,0 +1 @@ +["\u0060\u012a\u12AB"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_accepted_surrogate_pair.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_accepted_surrogate_pair.json new file mode 100755 index 00000000..996875cc --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_accepted_surrogate_pair.json @@ -0,0 +1 @@ +["\uD801\udc37"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_accepted_surrogate_pairs.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_accepted_surrogate_pairs.json new file mode 100755 index 00000000..3401021e --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_accepted_surrogate_pairs.json @@ -0,0 +1 @@ +["\ud83d\ude39\ud83d\udc8d"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_allowed_escapes.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_allowed_escapes.json new file mode 100644 index 00000000..7f495532 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_allowed_escapes.json @@ -0,0 +1 @@ +["\"\\\/\b\f\n\r\t"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_backslash_and_u_escaped_zero.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_backslash_and_u_escaped_zero.json new file mode 100755 index 00000000..d4439eda --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_backslash_and_u_escaped_zero.json @@ -0,0 +1 @@ +["\\u0000"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_backslash_doublequotes.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_backslash_doublequotes.json new file mode 100644 index 00000000..ae03243b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_backslash_doublequotes.json @@ -0,0 +1 @@ +["\""]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_comments.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_comments.json new file mode 100644 index 00000000..2260c20c --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_comments.json @@ -0,0 +1 @@ +["a/*b*/c/*d//e"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_double_escape_a.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_double_escape_a.json new file mode 100644 index 00000000..6715d6f4 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_double_escape_a.json @@ -0,0 +1 @@ +["\\a"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_double_escape_n.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_double_escape_n.json new file mode 100644 index 00000000..44ca56c4 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_double_escape_n.json @@ -0,0 +1 @@ +["\\n"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_escaped_control_character.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_escaped_control_character.json new file mode 100644 index 00000000..5b014a9c --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_escaped_control_character.json @@ -0,0 +1 @@ +["\u0012"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_escaped_noncharacter.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_escaped_noncharacter.json new file mode 100755 index 00000000..2ff52e2c --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_escaped_noncharacter.json @@ -0,0 +1 @@ +["\uFFFF"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_in_array.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_in_array.json new file mode 100755 index 00000000..21d7ae4c --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_in_array.json @@ -0,0 +1 @@ +["asd"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_in_array_with_leading_space.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_in_array_with_leading_space.json new file mode 100755 index 00000000..9e1887c1 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_in_array_with_leading_space.json @@ -0,0 +1 @@ +[ "asd"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_last_surrogates_1_and_2.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_last_surrogates_1_and_2.json new file mode 100644 index 00000000..3919cef7 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_last_surrogates_1_and_2.json @@ -0,0 +1 @@ +["\uDBFF\uDFFF"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_nbsp_uescaped.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_nbsp_uescaped.json new file mode 100644 index 00000000..2085ab1a --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_nbsp_uescaped.json @@ -0,0 +1 @@ +["new\u00A0line"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_nonCharacterInUTF-8_U+10FFFF.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_nonCharacterInUTF-8_U+10FFFF.json new file mode 100755 index 00000000..059e4d9d --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_nonCharacterInUTF-8_U+10FFFF.json @@ -0,0 +1 @@ +[""]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_nonCharacterInUTF-8_U+FFFF.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_nonCharacterInUTF-8_U+FFFF.json new file mode 100755 index 00000000..4c913bd4 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_nonCharacterInUTF-8_U+FFFF.json @@ -0,0 +1 @@ +[""]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_null_escape.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_null_escape.json new file mode 100644 index 00000000..c1ad8440 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_null_escape.json @@ -0,0 +1 @@ +["\u0000"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_one-byte-utf-8.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_one-byte-utf-8.json new file mode 100644 index 00000000..15718592 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_one-byte-utf-8.json @@ -0,0 +1 @@ +["\u002c"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_pi.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_pi.json new file mode 100644 index 00000000..9df11ae8 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_pi.json @@ -0,0 +1 @@ +["π"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_reservedCharacterInUTF-8_U+1BFFF.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_reservedCharacterInUTF-8_U+1BFFF.json new file mode 100755 index 00000000..10a33a17 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_reservedCharacterInUTF-8_U+1BFFF.json @@ -0,0 +1 @@ +[""]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_simple_ascii.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_simple_ascii.json new file mode 100644 index 00000000..8cadf7d0 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_simple_ascii.json @@ -0,0 +1 @@ +["asd "]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_space.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_space.json new file mode 100644 index 00000000..efd782cc --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_space.json @@ -0,0 +1 @@ +" "
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json new file mode 100755 index 00000000..7620b665 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json @@ -0,0 +1 @@ +["\uD834\uDd1e"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_three-byte-utf-8.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_three-byte-utf-8.json new file mode 100644 index 00000000..108f1d67 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_three-byte-utf-8.json @@ -0,0 +1 @@ +["\u0821"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_two-byte-utf-8.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_two-byte-utf-8.json new file mode 100644 index 00000000..461503c3 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_two-byte-utf-8.json @@ -0,0 +1 @@ +["\u0123"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_u+2028_line_sep.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_u+2028_line_sep.json new file mode 100755 index 00000000..897b6021 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_u+2028_line_sep.json @@ -0,0 +1 @@ +["
"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_u+2029_par_sep.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_u+2029_par_sep.json new file mode 100755 index 00000000..8cd998c8 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_u+2029_par_sep.json @@ -0,0 +1 @@ +["
"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_uEscape.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_uEscape.json new file mode 100755 index 00000000..f7b41a02 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_uEscape.json @@ -0,0 +1 @@ +["\u0061\u30af\u30EA\u30b9"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_uescaped_newline.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_uescaped_newline.json new file mode 100644 index 00000000..3a5a220b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_uescaped_newline.json @@ -0,0 +1 @@ +["new\u000Aline"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_unescaped_char_delete.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unescaped_char_delete.json new file mode 100755 index 00000000..7d064f49 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unescaped_char_delete.json @@ -0,0 +1 @@ +[""]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode.json new file mode 100644 index 00000000..3598095b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode.json @@ -0,0 +1 @@ +["\uA66D"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicodeEscapedBackslash.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicodeEscapedBackslash.json new file mode 100755 index 00000000..0bb3b51e --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicodeEscapedBackslash.json @@ -0,0 +1 @@ +["\u005C"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_2.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_2.json new file mode 100644 index 00000000..a7dcb976 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_2.json @@ -0,0 +1 @@ +["⍂㈴⍂"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+10FFFE_nonchar.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+10FFFE_nonchar.json new file mode 100644 index 00000000..9a8370b9 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+10FFFE_nonchar.json @@ -0,0 +1 @@ +["\uDBFF\uDFFE"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+1FFFE_nonchar.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+1FFFE_nonchar.json new file mode 100644 index 00000000..c51f8ae4 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+1FFFE_nonchar.json @@ -0,0 +1 @@ +["\uD83F\uDFFE"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json new file mode 100644 index 00000000..626d5f81 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json @@ -0,0 +1 @@ +["\u200B"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+2064_invisible_plus.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+2064_invisible_plus.json new file mode 100644 index 00000000..1e23972c --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+2064_invisible_plus.json @@ -0,0 +1 @@ +["\u2064"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+FDD0_nonchar.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+FDD0_nonchar.json new file mode 100644 index 00000000..18ef151b --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+FDD0_nonchar.json @@ -0,0 +1 @@ +["\uFDD0"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+FFFE_nonchar.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+FFFE_nonchar.json new file mode 100644 index 00000000..13d261fd --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_U+FFFE_nonchar.json @@ -0,0 +1 @@ +["\uFFFE"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_escaped_double_quote.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_escaped_double_quote.json new file mode 100755 index 00000000..4e625785 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_unicode_escaped_double_quote.json @@ -0,0 +1 @@ +["\u0022"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_utf8.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_utf8.json new file mode 100644 index 00000000..40878435 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_utf8.json @@ -0,0 +1 @@ +["€𝄞"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_string_with_del_character.json b/formats/json-tests/jvmTest/resources/spec_cases/y_string_with_del_character.json new file mode 100755 index 00000000..8bd24907 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_string_with_del_character.json @@ -0,0 +1 @@ +["aa"]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_false.json b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_false.json new file mode 100644 index 00000000..02e4a84d --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_false.json @@ -0,0 +1 @@ +false
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_int.json b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_int.json new file mode 100755 index 00000000..f70d7bba --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_int.json @@ -0,0 +1 @@ +42
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_negative_real.json b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_negative_real.json new file mode 100755 index 00000000..b5135a20 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_negative_real.json @@ -0,0 +1 @@ +-0.1
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_null.json b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_null.json new file mode 100644 index 00000000..ec747fa4 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_null.json @@ -0,0 +1 @@ +null
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_string.json b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_string.json new file mode 100755 index 00000000..b6e982ca --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_string.json @@ -0,0 +1 @@ +"asd"
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_true.json b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_true.json new file mode 100755 index 00000000..f32a5804 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_lonely_true.json @@ -0,0 +1 @@ +true
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_structure_string_empty.json b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_string_empty.json new file mode 100644 index 00000000..3cc762b5 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_string_empty.json @@ -0,0 +1 @@ +""
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_structure_trailing_newline.json b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_trailing_newline.json new file mode 100644 index 00000000..0c3426d4 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_trailing_newline.json @@ -0,0 +1 @@ +["a"] diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_structure_true_in_array.json b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_true_in_array.json new file mode 100644 index 00000000..de601e30 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_true_in_array.json @@ -0,0 +1 @@ +[true]
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/resources/spec_cases/y_structure_whitespace_array.json b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_whitespace_array.json new file mode 100644 index 00000000..2bedf7f2 --- /dev/null +++ b/formats/json-tests/jvmTest/resources/spec_cases/y_structure_whitespace_array.json @@ -0,0 +1 @@ + []
\ No newline at end of file diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/BigDecimalTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/BigDecimalTest.kt new file mode 100644 index 00000000..a0c4c73a --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/BigDecimalTest.kt @@ -0,0 +1,193 @@ +package kotlinx.serialization + +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.builtins.MapSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.* +import org.junit.Test +import java.math.BigDecimal + +private typealias BigDecimalKxs = @Serializable(with = BigDecimalNumericSerializer::class) BigDecimal + +class BigDecimalTest : JsonTestBase() { + + private val json = Json { + prettyPrint = true + } + + private inline fun <reified T> assertBigDecimalJsonFormAndRestored( + expected: String, + actual: T, + serializer: KSerializer<T> = serializer(), + ) = assertJsonFormAndRestored( + serializer, + actual, + expected, + json + ) + + @Test + fun bigDecimal() { + fun test(expected: String, actual: BigDecimal) = + assertBigDecimalJsonFormAndRestored(expected, actual, BigDecimalNumericSerializer) + + test("0", BigDecimal.ZERO) + test("1", BigDecimal.ONE) + test("-1", BigDecimal("-1")) + test("10", BigDecimal.TEN) + test(bdExpected1, bdActual1) + test(bdExpected2, bdActual2) + test(bdExpected3, bdActual3) + test(bdExpected4, bdActual4) + test(bdExpected5, bdActual5) + test(bdExpected6, bdActual6) + } + + @Test + fun bigDecimalList() { + + val bdList: List<BigDecimal> = listOf( + bdActual1, + bdActual2, + bdActual3, + bdActual4, + bdActual5, + bdActual6, + ) + + val expected = + """ + [ + $bdExpected1, + $bdExpected2, + $bdExpected3, + $bdExpected4, + $bdExpected5, + $bdExpected6 + ] + """.trimIndent() + + assertJsonFormAndRestored( + ListSerializer(BigDecimalNumericSerializer), + bdList, + expected, + json, + ) + } + + @Test + fun bigDecimalMap() { + val bdMap: Map<BigDecimal, BigDecimal> = mapOf( + bdActual1 to bdActual2, + bdActual3 to bdActual4, + bdActual5 to bdActual6, + ) + + val expected = + """ + { + "$bdExpected1": $bdExpected2, + "$bdExpected3": $bdExpected4, + "$bdExpected5": $bdExpected6 + } + """.trimIndent() + + assertJsonFormAndRestored( + MapSerializer(BigDecimalNumericSerializer, BigDecimalNumericSerializer), + bdMap, + expected, + json, + ) + } + + @Test + fun bigDecimalHolder() { + val bdHolder = BigDecimalHolder( + bd = bdActual1, + bdList = listOf( + bdActual1, + bdActual2, + bdActual3, + ), + bdMap = mapOf( + bdActual1 to bdActual2, + bdActual3 to bdActual4, + bdActual5 to bdActual6, + ), + ) + + val expected = + """ + { + "bd": $bdExpected1, + "bdList": [ + $bdExpected1, + $bdExpected2, + $bdExpected3 + ], + "bdMap": { + "$bdExpected1": $bdExpected2, + "$bdExpected3": $bdExpected4, + "$bdExpected5": $bdExpected6 + } + } + """.trimIndent() + + assertBigDecimalJsonFormAndRestored( + expected, + bdHolder, + ) + } + + companion object { + + // test data + private val bdActual1 = BigDecimal("725345854747326287606413621318.311864440287151714280387858224") + private val bdActual2 = BigDecimal("336052472523017262165484244513.836582112201211216526831524328") + private val bdActual3 = BigDecimal("211054843014778386028147282517.011200287614476453868782405400") + private val bdActual4 = BigDecimal("364751025728628060231208776573.207325218263752602211531367642") + private val bdActual5 = BigDecimal("508257556021513833656664177125.824502734715222686411316853148") + private val bdActual6 = BigDecimal("127134584027580606401102614002.366672301517071543257300444000") + + private const val bdExpected1 = "725345854747326287606413621318.311864440287151714280387858224" + private const val bdExpected2 = "336052472523017262165484244513.836582112201211216526831524328" + private const val bdExpected3 = "211054843014778386028147282517.011200287614476453868782405400" + private const val bdExpected4 = "364751025728628060231208776573.207325218263752602211531367642" + private const val bdExpected5 = "508257556021513833656664177125.824502734715222686411316853148" + private const val bdExpected6 = "127134584027580606401102614002.366672301517071543257300444000" + } + +} + +@Serializable +private data class BigDecimalHolder( + val bd: BigDecimalKxs, + val bdList: List<BigDecimalKxs>, + val bdMap: Map<BigDecimalKxs, BigDecimalKxs>, +) + +private object BigDecimalNumericSerializer : KSerializer<BigDecimal> { + + override val descriptor = PrimitiveSerialDescriptor("java.math.BigDecimal", PrimitiveKind.DOUBLE) + + override fun deserialize(decoder: Decoder): BigDecimal { + return if (decoder is JsonDecoder) { + BigDecimal(decoder.decodeJsonElement().jsonPrimitive.content) + } else { + BigDecimal(decoder.decodeString()) + } + } + + override fun serialize(encoder: Encoder, value: BigDecimal) { + val bdString = value.toPlainString() + + if (encoder is JsonEncoder) { + encoder.encodeJsonElement(JsonUnquotedLiteral(bdString)) + } else { + encoder.encodeString(bdString) + } + } +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/JavaCollectionsTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/JavaCollectionsTest.kt new file mode 100644 index 00000000..1f4958a6 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/JavaCollectionsTest.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2019 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlinx.serialization + +import kotlinx.serialization.json.Json +import kotlinx.serialization.test.typeTokenOf +import org.junit.Test +import java.util.HashMap +import java.util.HashSet +import kotlin.collections.LinkedHashMap +import kotlin.collections.Map +import kotlin.collections.hashMapOf +import kotlin.collections.hashSetOf +import kotlin.reflect.* +import kotlin.test.* + + +class JavaCollectionsTest { + @Serializable + data class HasHashMap( + val s: String, + val hashMap: HashMap<Int, String>, + val hashSet: HashSet<Int>, + val linkedHashMap: LinkedHashMap<Int, String>, + val kEntry: Map.Entry<Int, String>? + ) + + @Test + fun testJavaCollectionsInsideClass() { + val original = HasHashMap("42", hashMapOf(1 to "1", 2 to "2"), hashSetOf(11), LinkedHashMap(), null) + val serializer = HasHashMap.serializer() + val string = Json.encodeToString(serializer = serializer, value = original) + assertEquals( + expected = """{"s":"42","hashMap":{"1":"1","2":"2"},"hashSet":[11],"linkedHashMap":{},"kEntry":null}""", + actual = string + ) + val restored = Json.decodeFromString(deserializer = serializer, string = string) + assertEquals(expected = original, actual = restored) + } + + @Test + fun testTopLevelMaps() { + // Returning null here is a deliberate choice: map constructor functions may return different specialized + // implementations (e.g., kotlin.collections.EmptyMap or java.util.Collections.SingletonMap) + // that may or may not be generic. Since we generally cannot return a generic serializer using Java class only, + // all attempts to get map serializer using only .javaClass should return null. + assertNull(serializerOrNull(emptyMap<String, String>().javaClass)) + assertNull(serializerOrNull(mapOf<String, String>("a" to "b").javaClass)) + assertNull(serializerOrNull(mapOf<String, String>("a" to "b", "b" to "c").javaClass)) + // Correct ways of retrieving map serializer: + assertContains( + serializer(typeTokenOf<Map<String, String>>()).descriptor.serialName, + "kotlin.collections.LinkedHashMap" + ) + assertContains( + serializer(typeTokenOf<java.util.LinkedHashMap<String, String>>()).descriptor.serialName, + "kotlin.collections.LinkedHashMap" + ) + assertContains( + serializer(typeOf<LinkedHashMap<String, String>>()).descriptor.serialName, + "kotlin.collections.LinkedHashMap" + ) + } + + @Test + fun testTopLevelSetsAndLists() { + // Same reasoning as for maps + assertNull(serializerOrNull(emptyList<String>().javaClass)) + assertNull(serializerOrNull(listOf<String>("a").javaClass)) + assertNull(serializerOrNull(listOf<String>("a", "b").javaClass)) + assertNull(serializerOrNull(emptySet<String>().javaClass)) + assertNull(serializerOrNull(setOf<String>("a").javaClass)) + assertNull(serializerOrNull(setOf<String>("a", "b").javaClass)) + assertContains( + serializer(typeTokenOf<Set<String>>()).descriptor.serialName, + "kotlin.collections.LinkedHashSet" + ) + assertContains( + serializer(typeTokenOf<List<String>>()).descriptor.serialName, + "kotlin.collections.ArrayList" + ) + assertContains( + serializer(typeTokenOf<java.util.LinkedHashSet<String>>()).descriptor.serialName, + "kotlin.collections.LinkedHashSet" + ) + assertContains( + serializer(typeTokenOf<java.util.ArrayList<String>>()).descriptor.serialName, + "kotlin.collections.ArrayList" + ) + } + + @Test + fun testAnonymousObject() { + val obj: Any = object {} + assertNull(serializerOrNull(obj.javaClass)) + } +} + diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/JvmMissingFieldsExceptionTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/JvmMissingFieldsExceptionTest.kt new file mode 100644 index 00000000..a423bc84 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/JvmMissingFieldsExceptionTest.kt @@ -0,0 +1,138 @@ +package kotlinx.serialization + +import org.junit.Test +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.polymorphic +import kotlinx.serialization.modules.subclass +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertTrue + +class JvmMissingFieldsExceptionTest { + @Serializable + data class Generic<out T1, out T2, out T3>(val f1: T1, val f2: T2, val f3: T3) + + @Serializable + sealed class Parent(val p1: Int, val p2: Int = 2, val p3: Int) { + @Serializable + @SerialName("child") + data class Child(val c1: Int = 1, val c2: Int, val c3: Int = 3) : Parent(c1 + 1, 2, 3) + } + + @Serializable + open class ShortPlaneClass(val f1: Int, val f2: Int, val f3: Int = 3, val f4: Int) + + @Serializable + class WithTransient(val f1: Int, @Transient val f2: Int = 2, val f3: Int, val f4: Int) + + @Serializable + abstract class SimpleAbstract(val p1: Int, val p2: Int) + + @Serializable + @SerialName("a") + data class ChildA(val c1: Int, val c2: Int = 2, val c3: Int) : SimpleAbstract(0, 10) + + @Serializable + data class PolymorphicWrapper(@Polymorphic val nested: SimpleAbstract) + + @Serializable + class BigPlaneClass( + val f0: Int, + val f5: Int = 5, + val f6: Int, + val f7: Int = 7, + val f8: Int, + val f9: Int, + val f10: Int, + val f11: Int, + val f12: Int, + val f13: Int, + val f14: Int, + val f15: Int, + val f16: Int, + val f17: Int, + val f18: Int, + val f19: Int, + val f20: Int, + val f21: Int, + val f22: Int, + val f23: Int, + val f24: Int, + val f25: Int, + val f26: Int, + val f27: Int, + val f28: Int, + val f29: Int, + val f30: Int, + val f31: Int, + val f32: Int, + val f33: Int, + val f34: Int, + val f35: Int, + ) : ShortPlaneClass(1, 2, 3, 4) + + @Test + fun testShortPlaneClass() { + assertFailsWithMessages(listOf("f2", "f4")) { + Json.decodeFromString<ShortPlaneClass>("""{"f1":1}""") + } + } + + @Test + fun testBigPlaneClass() { + val missedFields = MutableList(36) { "f$it" } + val definedInJsonFields = arrayOf("f1", "f15", "f34") + val optionalFields = arrayOf("f3", "f5", "f7") + missedFields.removeAll(definedInJsonFields) + missedFields.removeAll(optionalFields) + assertFailsWithMessages(missedFields) { + Json.decodeFromString<BigPlaneClass>("""{"f1":1, "f15": 15, "f34": 34}""") + } + } + + @Test + fun testAnnotatedPolymorphic() { + val module = SerializersModule { + polymorphic(SimpleAbstract::class, null) { + subclass(ChildA::class) + } + } + + assertFailsWithMessages(listOf("p2", "c3")) { + Json { + serializersModule = module + }.decodeFromString<PolymorphicWrapper>("""{"nested": {"type": "a", "p1": 1, "c1": 11}}""") + } + } + + + @Test + fun testSealed() { + assertFailsWithMessages(listOf("p3", "c2")) { + Json.decodeFromString<Parent>("""{"type": "child", "p1":1, "c1": 11}""") + } + } + + @Test + fun testTransient() { + assertFailsWithMessages(listOf("f3", "f4")) { + Json.decodeFromString<WithTransient>("""{"f1":1}""") + } + } + + @Test + fun testGeneric() { + assertFailsWithMessages(listOf("f2", "f3")) { + Json.decodeFromString<Generic<Int, Int, Int>>("""{"f1":1}""") + } + } + + + private inline fun assertFailsWithMessages(fields: List<String>, block: () -> Unit) { + val exception = assertFailsWith(MissingFieldException::class, null, block) + val missedMessages = fields.filter { !exception.message!!.contains(it) } + assertEquals(exception.missingFields.sorted(), fields.sorted()) + assertTrue(missedMessages.isEmpty(), "Expected message '${exception.message}' to contain substrings $fields") + } +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/SerializationCasesTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/SerializationCasesTest.kt new file mode 100644 index 00000000..cbef36f3 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/SerializationCasesTest.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2018 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlinx.serialization + +import kotlinx.serialization.json.* +import org.junit.* +import org.junit.Assert.* + +class SerializationCasesTest : JsonTestBase() { + + @Serializable + data class Data1(val a: Int, val b: Int) + + @Serializer(forClass = Data1::class) + object ExtDataSerializer1 + + @Test + fun testConstructorValProperties() { + val data = Data1(1, 2) + + // Serialize with internal serializer for Data class + assertEquals("""{"a":1,"b":2}""", default.encodeToString(data)) + assertEquals(data, Json.decodeFromString<Data1>("""{"a":1,"b":2}""")) + + // Serialize with external serializer for Data class + assertEquals("""{"a":1,"b":2}""", default.encodeToString(ExtDataSerializer1, data)) + assertEquals(data, Json.decodeFromString(ExtDataSerializer1, """{"a":1,"b":2}""")) + } + + @Serializable + class Data2 { + var a = 0 + var b = 0 + override fun equals(other: Any?) = other is Data2 && other.a == a && other.b == b + } + + @Serializer(forClass=Data2::class) + object ExtDataSerializer2 + + @Test + fun testBodyVarProperties() { + val data = Data2().apply { + a = 1 + b = 2 + } + + // Serialize with internal serializer for Data class + assertEquals("""{"a":1,"b":2}""", default.encodeToString(data)) + assertEquals(data, Json.decodeFromString<Data2>("""{"a":1,"b":2}""")) + + // Serialize with external serializer for Data class + assertEquals("""{"a":1,"b":2}""", default.encodeToString(ExtDataSerializer2, data)) + assertEquals(data, Json.decodeFromString(ExtDataSerializer2, """{"a":1,"b":2}""")) + } + + enum class TintEnum { LIGHT, DARK } + + @Serializable + data class Data3( + val a: String, + val b: List<Int>, + val c: Map<String, TintEnum> + ) + + // Serialize with external serializer for Data class + @Serializer(forClass = Data3::class) + object ExtDataSerializer3 + + @Test + fun testNestedValues() { + val data = Data3("Str", listOf(1, 2), mapOf("lt" to TintEnum.LIGHT, "dk" to TintEnum.DARK)) + // Serialize with internal serializer for Data class + val expected = """{"a":"Str","b":[1,2],"c":{"lt":"LIGHT","dk":"DARK"}}""" + assertEquals(expected, default.encodeToString(data)) + assertEquals(data, Json.decodeFromString<Data3>(expected)) + assertEquals(expected, default.encodeToString(ExtDataSerializer3, data)) + assertEquals(data, Json.decodeFromString(ExtDataSerializer3, expected)) + } +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/SerializeJavaClassTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/SerializeJavaClassTest.kt new file mode 100644 index 00000000..a2f900d0 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/SerializeJavaClassTest.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization + +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.Json +import org.junit.Test +import java.text.DateFormat +import java.text.SimpleDateFormat +import java.util.* +import kotlin.test.assertEquals + +object DateSerializer : KSerializer<Date> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("java.util.Date", PrimitiveKind.STRING) + + // Consider wrapping in ThreadLocal if serialization may happen in multiple threads + private val df: DateFormat = SimpleDateFormat("dd/MM/yyyy HH:mm:ss.SSS").apply { + timeZone = TimeZone.getTimeZone("GMT+2") + } + + override fun serialize(encoder: Encoder, value: Date) { + encoder.encodeString(df.format(value)) + } + + override fun deserialize(decoder: Decoder): Date { + return df.parse(decoder.decodeString()) + } +} + +@Serializable +data class ClassWithDate(@Serializable(with = DateSerializer::class) val date: Date) + +class SerializeJavaClassTest { + @Test + fun serializeToStringAndRestore() { + // Thursday, 4 October 2018 09:00:00 GMT+02:00 — KotlinConf 2018 Keynote + val date = ClassWithDate(Date(1538636400000L)) + val s = Json.encodeToString(date) + assertEquals("""{"date":"04/10/2018 09:00:00.000"}""", s) + val date2 = Json.decodeFromString(ClassWithDate.serializer(), s) + assertEquals(date, date2) + } +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/SerializerByTypeCacheTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/SerializerByTypeCacheTest.kt new file mode 100644 index 00000000..b8dd35d1 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/SerializerByTypeCacheTest.kt @@ -0,0 +1,119 @@ +/* + * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization + +import kotlinx.serialization.json.Json +import java.net.URLClassLoader +import kotlin.reflect.* +import kotlin.test.* + +class SerializerByTypeCacheTest { + + @Serializable + class Holder(val i: Int) + + @Suppress("UNCHECKED_CAST") + @Test + fun testCaching() { + val typeOfKType = typeOf<Holder>() + val parameterKType = typeOf<List<Holder>>().arguments[0].type!! + assertSame(serializer(), serializer<Holder>()) + assertSame(serializer(typeOfKType), serializer(typeOfKType)) + assertSame(serializer(parameterKType), serializer(parameterKType)) + assertSame(serializer(), serializer(typeOfKType) as KSerializer<Holder>) + assertSame(serializer(parameterKType) as KSerializer<Holder>, serializer(typeOfKType) as KSerializer<Holder>) + } + + /** + * Checking the case when a parameterized type is loaded in different parallel [ClassLoader]s. + * + * If the main type is loaded by a common parent [ClassLoader] (for example, a bootstrap for [List]), + * and the element class is loaded by different loaders, then some implementations of the [KType] (e.g. `KTypeImpl` from reflection) may not see the difference between them. + * + * As a result, a serializer for another loader will be returned from the cache, and it will generate instances, when working with which we will get an [ClassCastException]. + * + * The test checks the correctness of the cache for such cases - that different serializers for different loaders will be returned. + * + * [see](https://youtrack.jetbrains.com/issue/KT-54523). + */ + @Test + fun testDifferentClassLoaders() { + val elementKType1 = SimpleKType(loadClass().kotlin) + val elementKType2 = SimpleKType(loadClass().kotlin) + + // Java class must be same (same name) + assertEquals(elementKType1.classifier.java.canonicalName, elementKType2.classifier.java.canonicalName) + // class loaders must be different + assertNotSame(elementKType1.classifier.java.classLoader, elementKType2.classifier.java.classLoader) + // due to the incorrect definition of the `equals`, KType-s are equal + assertEquals(elementKType1, elementKType2) + + // create parametrized type `List<Foo>` + val kType1 = SingleParametrizedKType(List::class, elementKType1) + val kType2 = SingleParametrizedKType(List::class, elementKType2) + + val serializer1 = serializer(kType1) + val serializer2 = serializer(kType2) + + // when taking a serializers from cache, we must distinguish between KType-s, despite the fact that they are equivalent + assertNotSame(serializer1, serializer2) + + // serializers must work correctly + Json.decodeFromString(serializer1, "[{\"i\":1}]") + Json.decodeFromString(serializer2, "[{\"i\":1}]") + } + + /** + * Load class `example.Foo` via new class loader. Compiled class-file located in the resources. + */ + private fun loadClass(): Class<*> { + val classesUrl = this::class.java.classLoader.getResource("class_loaders/classes/") + val loader1 = URLClassLoader(arrayOf(classesUrl), this::class.java.classLoader) + return loader1.loadClass("example.Foo") + } + + private class SimpleKType(override val classifier: KClass<*>): KType { + override val annotations: List<Annotation> = emptyList() + override val arguments: List<KTypeProjection> = emptyList() + + override val isMarkedNullable: Boolean = false + + override fun equals(other: Any?): Boolean { + if (other !is SimpleKType) return false + return classifier.java.canonicalName == other.classifier.java.canonicalName + } + + override fun hashCode(): Int { + return classifier.java.canonicalName.hashCode() + } + } + + + private class SingleParametrizedKType(override val classifier: KClass<*>, val parameterType: KType): KType { + override val annotations: List<Annotation> = emptyList() + + override val arguments: List<KTypeProjection> = listOf(KTypeProjection(KVariance.INVARIANT, parameterType)) + + override val isMarkedNullable: Boolean = false + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as SingleParametrizedKType + + if (classifier != other.classifier) return false + if (parameterType != other.parameterType) return false + + return true + } + + override fun hashCode(): Int { + var result = classifier.hashCode() + result = 31 * result + parameterType.hashCode() + return result + } + } +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/SerializerForNullableJavaTypeTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/SerializerForNullableJavaTypeTest.kt new file mode 100644 index 00000000..ebed6f37 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/SerializerForNullableJavaTypeTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization + +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import org.junit.Test +import java.util.* +import kotlin.test.* + +class SerializerForNullableJavaTypeTest { + + // User-reported generic serialization + class DateSerializer : KSerializer<Date?> { + + override val descriptor = PrimitiveSerialDescriptor("LocalTime?", PrimitiveKind.LONG).nullable + + override fun deserialize(decoder: Decoder): Date? = when (val seconds = decoder.decodeLong()) { + -1L -> null + else -> Date(seconds) + } + + override fun serialize(encoder: Encoder, value: Date?) { + when (value) { + null -> encoder.encodeLong(-1L) //this line is never reached despite that nulls exist in serialized lists + else -> encoder.encodeLong(value.toInstant().toEpochMilli()) + } + } + } + + @Serializable + private data class ListWrapper(val times: List<@Serializable(with = DateSerializer::class) Date?>) + + @Test + fun testMixedList() { + val data = ListWrapper(listOf(Date(42), null)) + val str = Json.encodeToString(data) + assertEquals("""{"times":[42,-1]}""", str) + assertEquals(data, Json.decodeFromString(str)) + } +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/StacktraceRecoveryTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/StacktraceRecoveryTest.kt new file mode 100644 index 00000000..dcf3e959 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/StacktraceRecoveryTest.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization + +import kotlinx.coroutines.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlinx.serialization.json.internal.* +import kotlinx.serialization.modules.* +import kotlin.test.* + +class StacktraceRecoveryTest { + @Serializable + private class Data(val s: String) + + private class BadDecoder : AbstractDecoder() { + override val serializersModule: SerializersModule = EmptySerializersModule() + override fun decodeElementIndex(descriptor: SerialDescriptor): Int = 42 + } + + @Test + fun testJsonDecodingException() = checkRecovered("JsonDecodingException") { + Json.decodeFromString<String>("42") + } + + @Test + fun testJsonEncodingException() = checkRecovered("JsonEncodingException") { + Json.encodeToString(Double.NaN) + } + + @Test + // checks simple name because UFE is internal class + fun testUnknownFieldException() = checkRecovered("UnknownFieldException") { + val serializer = Data.serializer() + serializer.deserialize(BadDecoder()) + } + + private fun checkRecovered(exceptionClassSimpleName: String, block: () -> Unit) = runBlocking { + val result = runCatching { + callBlockWithRecovery(block) + } + assertTrue(result.isFailure, "Block should have failed") + val e = result.exceptionOrNull()!! + assertEquals(exceptionClassSimpleName, e::class.simpleName!!) + val cause = e.cause + assertNotNull(cause, "Exception should have cause: $e") + assertEquals(e.message, cause.message) + assertEquals(exceptionClassSimpleName, e::class.simpleName!!) + } + + // KLUDGE: A separate function with state-machine to ensure coroutine DebugMetadata is generated. See KT-41789 + private suspend fun callBlockWithRecovery(block: () -> Unit) { + yield() + // use withContext to perform switch between coroutines and thus trigger exception recovery machinery + withContext(NonCancellable) { + block() + } + } +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/features/ContextualSerializationOnFileTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/ContextualSerializationOnFileTest.kt new file mode 100644 index 00000000..a190a483 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/ContextualSerializationOnFileTest.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// TODO: Move to common tests after https://youtrack.jetbrains.com/issue/KT-28927 is fixed + +@file:UseContextualSerialization(Int::class, IntHolder::class) + +package kotlinx.serialization.features + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlin.test.* + +@Serializable +data class Carrier3( + val a: IntHolder, + val i: Int, + val nullable: Int?, + val nullableIntHolder: IntHolder?, + val nullableIntList: List<Int?> = emptyList(), + val nullableIntHolderNullableList: List<IntHolder?>? = null +) + +class ContextualSerializationOnFileTest { + val module = SerializersModule { + contextual(DividingIntSerializer) + contextual(MultiplyingIntHolderSerializer) + } + val json = Json { serializersModule = module; encodeDefaults = true } + + @Test + fun testOnFile() { + val str = json.encodeToString(Carrier3.serializer(), Carrier3(IntHolder(42), 8, 8, IntHolder(42))) + assertEquals( + """{"a":84,"i":4,"nullable":4,"nullableIntHolder":84,"nullableIntList":[],"nullableIntHolderNullableList":null}""", + str + ) + } +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/features/InternalInheritanceTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/InternalInheritanceTest.kt new file mode 100644 index 00000000..6a1f722e --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/InternalInheritanceTest.kt @@ -0,0 +1,127 @@ +/* + * Copyright 2017-2019 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 org.junit.* +import org.junit.Assert.* + +class InternalInheritanceTest : JsonTestBase() { + @Serializable + open class A(val parent: Int) { + private val rootOptional = "rootOptional" + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is A) return false + + if (parent != other.parent) return false + if (rootOptional != other.rootOptional) return false + + return true + } + } + + @Serializable + open class B(val parent2: Int, @Transient val transientDerived: String = "X", val derived: String) : A(parent2) { + protected val bodyDerived = "body" + } + + @Serializable + class C(val parent3: Int) : B(parent3, derived = "derived") { + val lastDerived = "optional" + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other?.javaClass != javaClass) return false + + other as C + + if (!super.equals(other)) return false + if (parent3 != other.parent3) return false + if (lastDerived != other.lastDerived) return false + if (parent2 != other.parent2) return false + if (transientDerived != other.transientDerived) return false + if (derived != other.derived) return false + if (bodyDerived != other.bodyDerived) return false + + return true + } + } + + @Test + fun testEncodeToString() { + assertEquals( + """{"parent":42,"rootOptional":"rootOptional","parent2":42,"derived":"derived",""" + + """"bodyDerived":"body","parent3":42,"lastDerived":"optional"}""", + default.encodeToString(C(42)) + ) + assertEquals( + """{"parent":13,"rootOptional":"rootOptional","parent2":13,"derived":"bbb","bodyDerived":"body"}""", + default.encodeToString(B(13, derived = "bbb")) + ) + } + + @Test + fun testParse() { + assertEquals( + C(42), + default.decodeFromString<C>( + """{"parent":42,"rootOptional":"rootOptional","parent2":42,""" + + """"derived":"derived","bodyDerived":"body","parent3":42,"lastDerived":"optional"}""" + ) + ) + assertEquals( + C(43), + default.decodeFromString<C>("""{"parent":43,"rootOptional":"rootOptional","parent2":43,"derived":"derived",""" + + """"bodyDerived":"body","parent3":43,"lastDerived":"optional"}""") + ) + } + + @Test + fun testParseOptionals() { + assertEquals( + B(100, derived = "wowstring"), + default.decodeFromString<B>("""{"parent":100,"rootOptional":"rootOptional","parent2":100,"derived":"wowstring","bodyDerived":"body"}""") + ) + assertEquals( + C(44), + default.decodeFromString<C>("""{"parent":44, "parent2":44,"derived":"derived","bodyDerived":"body","parent3":44}""") + ) + assertEquals( + B(101, derived = "wowstring"), + default.decodeFromString<B>("""{"parent":101,"parent2":101,"derived":"wowstring","bodyDerived":"body"}""") + ) + assertEquals( + A(77), + default.decodeFromString<A>("""{"parent":77,"rootOptional":"rootOptional"}""") + ) + assertEquals( + A(78), + default.decodeFromString<A>("""{"parent":78}""") + ) + } + + @Test(expected = SerializationException::class) + fun testThrowTransient() { + Json.decodeFromString<B>("""{"parent":100,"rootOptional":"rootOptional","transientDerived":"X",""" + + """"parent2":100,"derived":"wowstring","bodyDerived":"body"}""") + } + + @Test(expected = SerializationException::class) + fun testThrowMissingField() { + default.decodeFromString<C>("""{"parent":42,"rootOptional":"rootOptional","derived":"derived",""" + + """"bodyDerived":"body","parent3":42,"lastDerived":"optional"}""") + } + + @Test + fun testSerializeAsParent() { + val obj1: A = B(77, derived = "derived") + val obj2: A = C(77) + assertEquals("""{"parent":77,"rootOptional":"rootOptional"}""", default.encodeToString(obj1)) + assertEquals("""{"parent":77,"rootOptional":"rootOptional"}""", default.encodeToString(obj2)) + } +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/features/JsonJvmStreamsTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/JsonJvmStreamsTest.kt new file mode 100644 index 00000000..7019edae --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/JsonJvmStreamsTest.kt @@ -0,0 +1,131 @@ +/* + * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") + +package kotlinx.serialization.features + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.json.* +import kotlinx.serialization.json.internal.BATCH_SIZE +import kotlinx.serialization.modules.* +import kotlinx.serialization.test.* +import org.junit.Test +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +class JsonJvmStreamsTest { + private val strLen = BATCH_SIZE * 2 + 42 + + @Test + fun testParsesStringsLongerThanBuffer() { + val str = "a".repeat(strLen) + val input = """{"data":"$str"}""" + assertEquals(input, Json.encodeViaStream(StringData.serializer(), StringData(str))) + assertEquals(str, Json.decodeViaStream(StringData.serializer(), input).data) + assertEquals(str, Json.decodeViaStream(String.serializer(), "\"$str\"")) + } + + @Test + fun testSkipsWhitespacesLongerThanBuffer() { + val str = "a".repeat(strLen) + val ws = " ".repeat(strLen) + val input = """{"data":$ws"$str"}""" + assertEquals("""{"data":"$str"}""", Json.encodeViaStream(StringData.serializer(), StringData(str))) + assertEquals(str, Json.decodeViaStream(StringData.serializer(), input).data) + } + + @Test + fun testHandlesEscapesLongerThanBuffer() { + val str = "\\t".repeat(strLen) + val expected = "\t".repeat(strLen) + val input = """{"data":"$str"}""" + assertEquals(input, Json.encodeViaStream(StringData.serializer(), StringData(expected))) + assertEquals(expected, Json.decodeViaStream(StringData.serializer(), input).data) + } + + @Test + fun testHandlesLongLenientStrings() { + val str = "a".repeat(strLen) + val input = """{"data":$str}""" + val json = Json { isLenient = true } + assertEquals(str, json.decodeViaStream(StringData.serializer(), input).data) + assertEquals(str, json.decodeViaStream(String.serializer(), str)) + } + + @Test + fun testThrowsCorrectExceptionOnEof() { + assertFailsWith<SerializationException> { + Json.decodeViaStream(StringData.serializer(), """{"data":""") + } + assertFailsWith<SerializationException> { + Json.decodeViaStream(StringData.serializer(), "") + } + assertFailsWith<SerializationException> { + Json.decodeViaStream(String.serializer(), "\"") + } + } + + @Test + fun testRandomEscapeSequences() { + repeat(1000) { + val s = generateRandomUnicodeString(strLen) + try { + val serializer = String.serializer() + val b = ByteArrayOutputStream() + Json.encodeToStream(serializer, s, b) + val restored = Json.decodeFromStream(serializer, ByteArrayInputStream(b.toByteArray())) + assertEquals(s, restored) + } catch (e: Throwable) { + // Not assertion error to preserve cause + throw IllegalStateException("Unexpectedly failed test, cause string: $s", e) + } + } + } + + interface Poly + + @Serializable + @SerialName("Impl") + data class Impl(val str: String) : Poly + + @Test + fun testPolymorphismWhenCrossingBatchSizeNonLeadingKey() { + val json = Json { + serializersModule = SerializersModule { + polymorphic(Poly::class) { + subclass(Impl::class, Impl.serializer()) + } + } + } + + val longString = "a".repeat(BATCH_SIZE - 5) + val string = """{"str":"$longString", "type":"Impl"}""" + val golden = Impl(longString) + + val deserialized = json.decodeViaStream(serializer<Poly>(), string) + assertEquals(golden, deserialized as Impl) + } + + @Test + fun testPolymorphismWhenCrossingBatchSize() { + val json = Json { + serializersModule = SerializersModule { + polymorphic(Poly::class) { + subclass(Impl::class, Impl.serializer()) + } + } + } + + val aLotOfWhiteSpaces = " ".repeat(BATCH_SIZE - 5) + val string = """{$aLotOfWhiteSpaces"type":"Impl", "str":"value"}""" + val golden = Impl("value") + + val deserialized = json.decodeViaStream(serializer<Poly>(), string) + assertEquals(golden, deserialized as Impl) + } +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/features/JsonLazySequenceTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/JsonLazySequenceTest.kt new file mode 100644 index 00000000..23887660 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/JsonLazySequenceTest.kt @@ -0,0 +1,197 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.features + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.runBlocking +import kotlinx.serialization.* +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.features.sealed.SealedChild +import kotlinx.serialization.features.sealed.SealedParent +import kotlinx.serialization.json.* +import kotlinx.serialization.test.assertFailsWithMessage +import kotlinx.serialization.test.assertFailsWithSerial +import org.junit.Test +import java.io.* +import kotlin.test.* + +class JsonLazySequenceTest { + val json = Json + + private suspend inline fun <reified T> Flow<T>.writeToStream(os: OutputStream) { + collect { + json.encodeToStream(it, os) + } + } + + private suspend inline fun <reified T> Json.readFromStream(iss: InputStream): Flow<T> = flow { + val serial = serializer<T>() + val iter = iterateOverStream(iss, serial) + while (iter.hasNext()) { + emit(iter.next()) + } + }.flowOn(Dispatchers.IO) + + private val inputStringWsSeparated = """{"data":"a"}{"data":"b"}{"data":"c"}""" + private val inputStringWrapped = """[{"data":"a"},{"data":"b"},{"data":"c"}]""" + private val inputList = listOf(StringData("a"), StringData("b"), StringData("c")) + + @Test + fun testEncodeSeveralItems() { + val items = inputList + val os = ByteArrayOutputStream() + runBlocking { + val f = flow<StringData> { items.forEach { emit(it) } } + f.writeToStream(os) + } + + assertEquals(inputStringWsSeparated, os.toString(Charsets.UTF_8.name())) + } + + @Test + fun testDecodeSeveralItems() { + val ins = ByteArrayInputStream(inputStringWsSeparated.encodeToByteArray()) + assertFailsWithMessage<SerializationException>("EOF") { + json.decodeFromStream<StringData>(ins) + } + } + + private inline fun <reified T> Iterator<T>.assertNext(expected: T) { + assertTrue(hasNext()) + assertEquals(expected, next()) + } + + private fun <T> Json.iterateOverStream(stream: InputStream, deserializer: DeserializationStrategy<T>): Iterator<T> = + decodeToSequence(stream, deserializer).iterator() + + private fun withInputs(vararg inputs: String = arrayOf(inputStringWsSeparated, inputStringWrapped), block: (InputStream) -> Unit) { + for (input in inputs) { + val res = runCatching { block(input.asInputStream()) } + if (res.isFailure) throw AssertionError("Failed test with input $input", res.exceptionOrNull()) + } + } + + private fun String.asInputStream() = ByteArrayInputStream(this.encodeToByteArray()) + + @Test + fun testIterateSeveralItems() = withInputs { ins -> + val iter = json.iterateOverStream(ins, StringData.serializer()) + iter.assertNext(StringData("a")) + iter.assertNext(StringData("b")) + iter.assertNext(StringData("c")) + assertFalse(iter.hasNext()) + assertFalse(iter.hasNext()) // Subsequent calls to .hasNext() should not throw EOF or anything + assertFailsWithMessage<SerializationException>("EOF") { + iter.next() + } + } + + @Test + fun testDecodeToSequence() = withInputs { ins -> + val sequence = json.decodeToSequence(ins, StringData.serializer()) + assertEquals(inputList, sequence.toList(), "For input $inputStringWsSeparated") + assertFailsWith<IllegalStateException> { sequence.toList() } // assert constrained once + } + + @Test + fun testDecodeAsFlow() = withInputs { ins -> + val list = runBlocking { + buildList { json.readFromStream<StringData>(ins).toCollection(this) } + } + assertEquals(inputList, list) + } + + @Test + fun testItemsSeparatedByWs() { + val input = "{\"data\":\"a\"} {\"data\":\"b\"}\n\t{\"data\":\"c\"}" + val ins = ByteArrayInputStream(input.encodeToByteArray()) + assertEquals(inputList, json.decodeToSequence(ins, StringData.serializer()).toList()) + } + + @Test + fun testJsonElement() { + val list = listOf<JsonElement>( + buildJsonObject { put("foo", "bar") }, + buildJsonObject { put("foo", "baz") }, + JsonPrimitive(10), + JsonPrimitive("abacaba"), + buildJsonObject { put("foo", "qux") } + ) + val inputWs = """${list[0]} ${list[1]} ${list[2]} ${list[3]} ${list[4]}""" + val decodedWs = json.decodeToSequence<JsonElement>(inputWs.asInputStream()).toList() + assertEquals(list, decodedWs, "Failed whitespace-separated test") + val inputArray = """[${list[0]}, ${list[1]},${list[2]} , ${list[3]} ,${list[4]}]""" + val decodedArrayWrap = json.decodeToSequence<JsonElement>(inputArray.asInputStream()).toList() + assertEquals(list, decodedArrayWrap, "Failed array-wrapped test") + } + + + @Test + fun testSealedClasses() { + val input = """{"type":"first child","i":1,"j":10} {"type":"first child","i":1,"j":11}""" + val iter = json.iterateOverStream(input.asInputStream(), SealedParent.serializer()) + iter.assertNext(SealedChild(10)) + iter.assertNext(SealedChild(11)) + } + + @Test + fun testMalformedArray() { + val input1 = """[1, 2, 3""" + val input2 = """[1, 2, 3]qwert""" + val input3 = """[1,2 3]""" + withInputs(input1, input2, input3) { + assertFailsWithSerial("JsonDecodingException") { + json.decodeToSequence(it, Int.serializer()).toList() + } + } + } + + @Test + fun testMultilineArrays() { + val input = "[1,2,3]\n[4,5,6]\n[7,8,9]" + assertFailsWithSerial("JsonDecodingException") { + json.decodeToSequence<List<Int>>(input.asInputStream(), DecodeSequenceMode.AUTO_DETECT).toList() + } + assertFailsWithSerial("JsonDecodingException") { + json.decodeToSequence<Int>(input.asInputStream(), DecodeSequenceMode.AUTO_DETECT).toList() + } + assertFailsWithSerial("JsonDecodingException") { // we do not merge lists + json.decodeToSequence<Int>(input.asInputStream(), DecodeSequenceMode.ARRAY_WRAPPED).toList() + } + val parsed = json.decodeToSequence<List<Int>>(input.asInputStream(), DecodeSequenceMode.WHITESPACE_SEPARATED).toList() + val expected = listOf(listOf(1,2,3), listOf(4,5,6), listOf(7,8,9)) + assertEquals(expected, parsed) + } + + @Test + fun testStrictArrayCheck() { + assertFailsWithSerial("JsonDecodingException") { + json.decodeToSequence<StringData>(inputStringWsSeparated.asInputStream(), DecodeSequenceMode.ARRAY_WRAPPED) + } + } + + @Test + fun testPaddedWs() { + val paddedWs = " $inputStringWsSeparated " + assertEquals(inputList, json.decodeToSequence(paddedWs.asInputStream(), StringData.serializer()).toList()) + assertEquals(inputList, json.decodeToSequence(paddedWs.asInputStream(), StringData.serializer(), DecodeSequenceMode.WHITESPACE_SEPARATED).toList()) + } + + @Test + fun testPaddedArray() { + val paddedWs = " $inputStringWrapped " + assertEquals(inputList, json.decodeToSequence(paddedWs.asInputStream(), StringData.serializer()).toList()) + assertEquals(inputList, json.decodeToSequence(paddedWs.asInputStream(), StringData.serializer(), DecodeSequenceMode.ARRAY_WRAPPED).toList()) + } + + @Test + fun testToIteratorAndBack() = withInputs { ins -> + val iterator = Json.decodeToSequence(ins, StringData.serializer()).iterator() + val values = iterator.asSequence().take(3).toList() + assertEquals(inputList, values) + assertFalse(iterator.hasNext()) + } +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/features/JsonSequencePathTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/JsonSequencePathTest.kt new file mode 100644 index 00000000..554deab1 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/JsonSequencePathTest.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2017-2021 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.Serializable +import kotlinx.serialization.json.* +import kotlinx.serialization.test.assertFailsWithMessage +import org.junit.Test +import java.io.* + +class JsonSequencePathTest { + + @Serializable + class NestedData(val s: String) + + @Serializable + class Data(val data: NestedData) + + @Test + fun testFailure() { + val source = """{"data":{"s":"value"}}{"data":{"s":42}}{notevenreached}""".toStream() + val iterator = Json.decodeToSequence<Data>(source).iterator() + iterator.next() // Ignore + assertFailsWithMessage<SerializationException>( + "Expected quotation mark '\"', but had '4' instead at path: \$.data.s" + ) { iterator.next() } + } + + private fun String.toStream() = ByteArrayInputStream(encodeToByteArray()) +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt new file mode 100644 index 00000000..a600b9d7 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt @@ -0,0 +1,292 @@ +/* + * Copyright 2017-2021 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.builtins.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlinx.serialization.test.* +import org.junit.Test +import java.lang.reflect.* +import kotlin.reflect.* +import kotlin.test.* +import kotlin.time.* + +class SerializerByTypeTest { + + private val json = Json + + @Serializable + data class Box<out T>(val a: T) + + @Serializable + data class Data(val l: List<String>, val b: Box<Int>) + + @Serializable(WithCustomDefault.Companion::class) + data class WithCustomDefault(val n: Int) { + + companion object: KSerializer<WithCustomDefault> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("WithCustomDefault", PrimitiveKind.INT) + override fun serialize(encoder: Encoder, value: WithCustomDefault) = encoder.encodeInt(value.n) + override fun deserialize(decoder: Decoder) = WithCustomDefault(decoder.decodeInt()) + } + } + + object IntBoxToken : ParameterizedType { + override fun getRawType() = Box::class.java + override fun getOwnerType() = null + override fun getActualTypeArguments(): Array<Type> = arrayOf(Int::class.java) + } + + @Serializable + object SerializableObject + + @Serializable + data class WithNamedCompanion(val a: Int) { + companion object Named + } + + @Test + fun testGenericParameter() { + val b = Box(42) + assertSerializedWithType(IntBoxToken, """{"a":42}""", b) + } + + @Test + fun testNestedGenericParameter() { + val b = Box(Box(239)) + assertSerializedWithType(typeTokenOf<Box<Box<Int>>>(), """{"a":{"a":239}}""", b) + } + + @Test + fun testArray() { + val myArr = arrayOf("a", "b", "c") + val token = myArr::class.java + assertSerializedWithType(token, """["a","b","c"]""", myArr) + } + + @Test + fun testList() { + val myArr = listOf("a", "b", "c") + val token = object : ParameterizedType { + override fun getRawType(): Type = List::class.java + override fun getOwnerType(): Type? = null + override fun getActualTypeArguments(): Array<Type> = arrayOf(String::class.java) + } + assertSerializedWithType(token, """["a","b","c"]""", myArr) + } + + @Test + fun testListAsCollection() { + val myArr: Collection<String> = listOf("a", "b", "c") + val token = object : ParameterizedType { + override fun getRawType(): Type = Collection::class.java + override fun getOwnerType(): Type? = null + override fun getActualTypeArguments(): Array<Type> = arrayOf(String::class.java) + } + assertSerializedWithType(token, """["a","b","c"]""", myArr) + } + + + @Test + fun testReifiedArrayResolving() { + val myArr = arrayOf("a", "b", "c") + val token = typeTokenOf<Array<String>>() + assertSerializedWithType(token, """["a","b","c"]""", myArr) + } + + @Test + fun testPrimitiveArrayResolving() { + val myArr = intArrayOf(1, 2, 3) + val token = IntArray::class.java + val name = serializer(token).descriptor.serialName + assertTrue(name.contains("IntArray")) + assertSerializedWithType(token, """[1,2,3]""", myArr) + } + + @Test + fun testReifiedListResolving() { + val myList = listOf("a", "b", "c") + val token = typeTokenOf<List<String>>() + assertSerializedWithType(token, """["a","b","c"]""", myList) + } + + @Test + fun testReifiedSetResolving() { + val mySet = setOf("a", "b", "c", "c") + val token = typeTokenOf<Set<String>>() + assertSerializedWithType(token, """["a","b","c"]""", mySet) + } + + @Test + fun testReifiedMapResolving() { + val myMap = mapOf("a" to Data(listOf("c"), Box(6))) + val token = typeTokenOf<Map<String, Data>>() + assertSerializedWithType(token, """{"a":{"l":["c"],"b":{"a":6}}}""",myMap) + } + + @Test + fun testNestedListResolving() { + val myList = listOf(listOf(listOf(1, 2, 3)), listOf()) + val token = typeTokenOf<List<List<List<Int>>>>() + assertSerializedWithType(token, "[[[1,2,3]],[]]", myList) + } + + @Test + fun testNestedArrayResolving() { + val myList = arrayOf(arrayOf(arrayOf(1, 2, 3)), arrayOf()) + val token = typeTokenOf<Array<Array<Array<Int>>>>() + assertSerializedWithType(token, "[[[1,2,3]],[]]", myList) + } + + @Test + fun testNestedMixedResolving() { + val myList = arrayOf(listOf(arrayOf(1, 2, 3)), listOf()) + val token = typeTokenOf<Array<List<Array<Int>>>>() + assertSerializedWithType(token, "[[[1,2,3]],[]]", myList) + } + + @Test + fun testPair() { + val myPair = "42" to 42 + val token = typeTokenOf<Pair<String, Int>>() + assertSerializedWithType(token, """{"first":"42","second":42}""", myPair) + } + + @Test + fun testTriple() { + val myTriple = Triple("1", 2, Box(42)) + val token = typeTokenOf<Triple<String, Int, Box<Int>>>() + assertSerializedWithType(token, """{"first":"1","second":2,"third":{"a":42}}""", myTriple) + } + + @Test + fun testGenericInHolder() { + val b = Data(listOf("a", "b", "c"), Box(42)) + assertSerializedWithType(Data::class.java,"""{"l":["a","b","c"],"b":{"a":42}}""", b ) + } + + @Test + fun testOverriddenSerializer() { + val foo = json.decodeFromString<WithCustomDefault>("9") + assertEquals(9, foo.n) + } + + @Test + fun testNamedCompanion() { + val namedCompanion = WithNamedCompanion(1) + assertSerializedWithType(WithNamedCompanion::class.java, """{"a":1}""", namedCompanion) + } + + @Test + fun testPrimitive() { + val token = typeTokenOf<Int>() + val serial = serializer(token) + assertSame(Int.serializer() as KSerializer<*>, serial) + } + + @Test + fun testObject() { + val token = typeTokenOf<SerializableObject>() + val serial = serializer(token) + assertEquals(SerializableObject.serializer().descriptor, serial.descriptor) + } + + @Suppress("UNCHECKED_CAST") + private fun <T> assertSerializedWithType( + token: Type, + expected: String, + value: T, + ) { + val serial = serializer(token) as KSerializer<T> + assertEquals(expected, json.encodeToString(serial, value)) + val serial2 = requireNotNull(serializerOrNull(token)) { "Expected serializer to be found" } + assertEquals(expected, json.encodeToString(serial2 as KSerializer<T>, value)) + } + + class IntBox(val i: Int) + + object CustomIntSerializer : KSerializer<IntBox> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("CIS", PrimitiveKind.INT) + + override fun serialize(encoder: Encoder, value: IntBox) { + encoder.encodeInt(42) + } + + override fun deserialize(decoder: Decoder): IntBox { + TODO() + } + } + + @Test + fun testContextualLookup() { + val module = SerializersModule { contextual(CustomIntSerializer) } + val serializer = module.serializer(typeTokenOf<List<List<IntBox>>>()) + assertEquals("[[42]]", Json.encodeToString(serializer, listOf(listOf(IntBox(1))))) + } + + @Test + fun testCompiledWinsOverContextual() { + val contextual = object : KSerializer<Int> { + override val descriptor: SerialDescriptor = Int.serializer().descriptor + + override fun serialize(encoder: Encoder, value: Int) { + fail() + } + + override fun deserialize(decoder: Decoder): Int { + fail() + } + } + val module = SerializersModule { contextual(contextual) } + val serializer = module.serializer(typeTokenOf<List<List<Int>>>()) + assertEquals("[[1]]", Json.encodeToString(serializer, listOf(listOf<Int>(1)))) + assertEquals("42", Json.encodeToString(module.serializer(typeTokenOf<Int>()), 42)) + } + + class NonSerializable + + class NonSerializableBox<T>(val boxed: T) + + @Test + fun testLookupFail() { + assertNull(serializerOrNull(typeTokenOf<NonSerializable>())) + assertNull(serializerOrNull(typeTokenOf<NonSerializableBox<String>>())) + assertNull(serializerOrNull(typeTokenOf<Box<NonSerializable>>())) + + assertFailsWithMessage<SerializationException>("for class 'NonSerializable'") { + serializer(typeTokenOf<NonSerializable>()) + } + + assertFailsWithMessage<SerializationException>("for class 'NonSerializableBox'") { + serializer(typeTokenOf<NonSerializableBox<String>>()) + } + + assertFailsWithMessage<SerializationException>("for class 'NonSerializable'") { + serializer(typeTokenOf<kotlinx.serialization.Box<NonSerializable>>()) + } + + assertFailsWithMessage<SerializationException>("for class 'NonSerializable'") { + serializer(typeTokenOf<Array<NonSerializable>>()) + } + } + + @OptIn(ExperimentalTime::class) + @Test + fun testSerializersAreIntrinsified() { + val direct = measureTime { + Json.encodeToString(IntData.serializer(), IntData(10)) + } + val directMs = direct.inWholeMicroseconds + val indirect = measureTime { + Json.encodeToString(IntData(10)) + } + val indirectMs = indirect.inWholeMicroseconds + if (indirectMs > directMs + (directMs / 4)) error("Direct ($directMs) and indirect ($indirectMs) times are too far apart") + } +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/json/GsonCompatibilityTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/json/GsonCompatibilityTest.kt new file mode 100644 index 00000000..99bc23f9 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/json/GsonCompatibilityTest.kt @@ -0,0 +1,56 @@ +package kotlinx.serialization.json + +import com.google.gson.* +import kotlinx.serialization.* +import org.junit.Test +import kotlin.test.* + +class GsonCompatibilityTest { + + @Serializable + data class Box(val d: Double, val f: Float) + + @Test + fun testNaN() { + checkCompatibility(Box(Double.NaN, 1.0f)) + checkCompatibility(Box(1.0, Float.NaN)) + checkCompatibility(Box(Double.NaN, Float.NaN)) + } + + @Test + fun testInfinity() { + checkCompatibility(Box(Double.POSITIVE_INFINITY, 1.0f)) + checkCompatibility(Box(1.0, Float.POSITIVE_INFINITY)) + checkCompatibility(Box(Double.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY)) + } + + @Test + fun testNumber() { + checkCompatibility(Box(23.9, 23.9f)) + } + + private fun checkCompatibility(box: Box) { + checkCompatibility(box, Gson(), Json) + checkCompatibility(box, GsonBuilder().serializeSpecialFloatingPointValues().create(), Json { allowSpecialFloatingPointValues = true }) + } + + private fun checkCompatibility(box: Box, gson: Gson, json: Json) { + val jsonResult = resultOrNull { json.encodeToString(box) } + val gsonResult = resultOrNull { gson.toJson(box) } + assertEquals(gsonResult, jsonResult) + + if (jsonResult != null && gsonResult != null) { + val jsonDeserialized: Box = json.decodeFromString(jsonResult) + val gsonDeserialized: Box = gson.fromJson(gsonResult, Box::class.java) + assertEquals(gsonDeserialized, jsonDeserialized) + } + } + + private fun resultOrNull(function: () -> String): String? { + return try { + function() + } catch (t: Throwable) { + null + } + } +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/json/JsonChunkedBase64DecoderTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/json/JsonChunkedBase64DecoderTest.kt new file mode 100644 index 00000000..35dc16fc --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/json/JsonChunkedBase64DecoderTest.kt @@ -0,0 +1,85 @@ +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.test.assertFailsWithMessage +import org.junit.Test +import java.io.* +import java.util.* +import kotlin.random.Random +import kotlin.test.* + + +@Serializable(with = LargeBase64StringSerializer::class) +data class LargeBinaryData(val binaryData: ByteArray) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as LargeBinaryData + + if (!binaryData.contentEquals(other.binaryData)) return false + + return true + } + + override fun hashCode(): Int { + return binaryData.contentHashCode() + } +} + +@Serializable +data class ClassWithBinaryDataField(val binaryField: LargeBinaryData) + +object LargeBase64StringSerializer : KSerializer<LargeBinaryData> { + private val b64Decoder: Base64.Decoder = Base64.getDecoder() + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LargeStringContent", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): LargeBinaryData { + require(decoder is ChunkedDecoder) { "Only chunked decoder supported" } + + var reminder = "" + val decodedBytes = ByteArrayOutputStream().use { bos -> + decoder.decodeStringChunked { + val actualChunk = reminder + it + val reminderLength = actualChunk.length % 4 + val alignedLength = actualChunk.length - reminderLength + val alignedChunk = actualChunk.take(alignedLength) + reminder = actualChunk.takeLast(reminderLength) + bos.write(b64Decoder.decode(alignedChunk)) + } + bos.toByteArray() + } + + return LargeBinaryData(decodedBytes) + } + + override fun serialize(encoder: Encoder, value: LargeBinaryData) { + encoder.encodeString(Base64.getEncoder().encodeToString(value.binaryData)) + } +} + +class JsonChunkedBase64DecoderTest : JsonTestBase() { + + @Test + fun decodeBase64String() { + val sourceObject = + ClassWithBinaryDataField(LargeBinaryData(Random.nextBytes(16 * 1024))) // After encoding to Base64 will be larger than 16k (JsonLexer#BATCH_SIZE) + val serializedObject = Json.encodeToString(sourceObject) + + JsonTestingMode.values().forEach { mode -> + if (mode == JsonTestingMode.TREE) { + assertFailsWithMessage<IllegalArgumentException>( + "Only chunked decoder supported", "Shouldn't decode JSON in TREE mode" + ) { + Json.decodeFromString<ClassWithBinaryDataField>(serializedObject, mode) + } + } else { + val deserializedObject = Json.decodeFromString<ClassWithBinaryDataField>(serializedObject, mode) + assertEquals(sourceObject.binaryField, deserializedObject.binaryField) + } + } + } +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/json/JsonConcurrentStressTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/json/JsonConcurrentStressTest.kt new file mode 100644 index 00000000..cdcbab79 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/json/JsonConcurrentStressTest.kt @@ -0,0 +1,78 @@ +package kotlinx.serialization.json + +import kotlinx.coroutines.* +import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.* +import org.junit.Test +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import kotlin.random.* +import kotlin.test.* + +// Stresses out that JSON decoded in parallel does not interfere (mostly via caching of various buffers) +class JsonConcurrentStressTest : JsonTestBase() { + private val charset = "ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz0123456789" + + @Test + fun testDecodeInParallelSimpleList() = doTest(100) { mode -> + val value = (1..10000).map { Random.nextDouble() } + val string = Json.encodeToString(ListSerializer(Double.serializer()), value, mode) + assertEquals(value, Json.decodeFromString(ListSerializer(Double.serializer()), string, mode)) + } + + @Serializable + data class Foo(val s: String, val f: Foo?) + + @Test + fun testDecodeInParallelListOfPojo() = doTest(1_000) { mode -> + val value = (1..100).map { + val randomString = getRandomString() + val nestedFoo = Foo("null抢\u000E鋽윝䑜厼\uF70A紲ᢨ䣠null⛾䉻嘖緝ᯧnull쎶\u0005null" + randomString, null) + Foo(getRandomString(), nestedFoo) + } + val string = Json.encodeToString(ListSerializer(Foo.serializer()), value, mode) + assertEquals(value, Json.decodeFromString(ListSerializer(Foo.serializer()), string, mode)) + } + + @Test + fun testDecodeInParallelPojo() = doTest(100_000) { mode -> + val randomString = getRandomString() + val nestedFoo = Foo("null抢\u000E鋽윝䑜厼\uF70A紲ᢨ䣠null⛾䉻嘖緝ᯧnull쎶\u0005null" + randomString, null) + val randomFoo = Foo(getRandomString(), nestedFoo) + val string = Json.encodeToString(Foo.serializer(), randomFoo, mode) + assertEquals(randomFoo, Json.decodeFromString(Foo.serializer(), string, mode)) + } + + @Test + fun testDecodeInParallelSequencePojo() = runBlocking<Unit> { + for (i in 1 until 1_000) { + launch(Dispatchers.Default) { + val values = (1..100).map { + val randomString = getRandomString() + val nestedFoo = Foo("null抢\u000E鋽윝䑜厼\uF70A紲ᢨ䣠null⛾䉻嘖緝ᯧnull쎶\u0005null" + randomString, null) + Foo(getRandomString(), nestedFoo) + } + val baos = ByteArrayOutputStream() + for (value in values) { + Json.encodeToStream(Foo.serializer(), value, baos) + } + val bais = ByteArrayInputStream(baos.toByteArray()) + assertEquals(values, Json.decodeToSequence(bais, Foo.serializer()).toList()) + } + } + } + + private fun getRandomString() = (1..Random.nextInt(0, charset.length)).map { charset[it] }.joinToString(separator = "") + + private fun doTest(iterations: Int, block: (JsonTestingMode) -> Unit) { + runBlocking<Unit> { + for (i in 1 until iterations) { + launch(Dispatchers.Default) { + parametrizedTest { + block(it) + } + } + } + } + } +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/json/MissingFieldExceptionWithPathTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/json/MissingFieldExceptionWithPathTest.kt new file mode 100644 index 00000000..e56f173e --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/json/MissingFieldExceptionWithPathTest.kt @@ -0,0 +1,40 @@ +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlinx.serialization.json.Json.Default.decodeFromString +import org.junit.* +import org.junit.Test +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.PrintWriter +import java.io.StringWriter +import kotlin.test.* + +class MissingFieldExceptionWithPathTest { + + @Test // Repro for #2212 + fun testMfeIsNotReappliedMultipleTimes() { + val inputMalformed = """{"title": "...","cast": [{}]""" + try { + Json.decodeFromString<Movie>(inputMalformed) + fail("Unreacheable state") + } catch (e: MissingFieldException) { + val fullStackTrace = e.stackTraceToString() + val i1 = fullStackTrace.toString().indexOf("at path") + val i2 = fullStackTrace.toString().lastIndexOf("at path") + assertEquals(i1, i2) + assertTrue(i1 != -1) + } + } + + @Serializable + data class Movie( + val title: String, + val cast: List<Cast>, + ) + + @Serializable + data class Cast( + val name: String + ) +} diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/json/SpecConformanceTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/json/SpecConformanceTest.kt Binary files differnew file mode 100644 index 00000000..96401f72 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/json/SpecConformanceTest.kt diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/test/CurrentPlatform.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/test/CurrentPlatform.kt new file mode 100644 index 00000000..91aa17f0 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/test/CurrentPlatform.kt @@ -0,0 +1,8 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.test + + +public actual val currentPlatform: Platform = Platform.JVM diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/test/JsonHelpers.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/test/JsonHelpers.kt new file mode 100644 index 00000000..9220bbd3 --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/test/JsonHelpers.kt @@ -0,0 +1,20 @@ +package kotlinx.serialization.test + +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.json.* +import java.io.ByteArrayOutputStream + +actual fun <T> Json.encodeViaStream( + serializer: SerializationStrategy<T>, + value: T +): String { + val output = ByteArrayOutputStream() + encodeToStream(serializer, value, output) + return output.toString(Charsets.UTF_8.name()) +} + +actual fun <T> Json.decodeViaStream( + serializer: DeserializationStrategy<T>, + input: String +): T = decodeFromStream(serializer, input.byteInputStream()) diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/test/TypeToken.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/test/TypeToken.kt new file mode 100644 index 00000000..2b04274c --- /dev/null +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/test/TypeToken.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.test + +import java.lang.reflect.* + + +@PublishedApi +internal open class TypeBase<T> + +public inline fun <reified T> typeTokenOf(): Type { + val base = object : TypeBase<T>() {} + val superType = base::class.java.genericSuperclass!! + return (superType as ParameterizedType).actualTypeArguments.first()!! +} diff --git a/formats/json-tests/nativeTest/src/kotlinx/serialization/json/MultiWorkerJsonTest.kt b/formats/json-tests/nativeTest/src/kotlinx/serialization/json/MultiWorkerJsonTest.kt new file mode 100644 index 00000000..2ea063db --- /dev/null +++ b/formats/json-tests/nativeTest/src/kotlinx/serialization/json/MultiWorkerJsonTest.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.json + +import kotlinx.serialization.* +import kotlin.native.concurrent.* +import kotlin.test.* + +class MultiWorkerJsonTest { + @Serializable + data class PlainOne(val one: Int) + + @Serializable + data class PlainTwo(val two: Int) + + private fun doTest(json: () -> Json) { + val worker = Worker.start() + val operation = { + for (i in 0..999) { + assertEquals(PlainOne(42), json().decodeFromString("""{"one":42,"two":239}""")) + } + } + worker.executeAfter(1000, operation.freeze()) + for (i in 0..999) { + assertEquals(PlainTwo(239), json().decodeFromString("""{"one":42,"two":239}""")) + } + worker.requestTermination() + } + + + @Test + fun testJsonIsFreezeSafe() { + val json = Json { + isLenient = true + ignoreUnknownKeys = true + useAlternativeNames = true + } + // reuse instance + doTest { json } + } + + @Test + fun testJsonInstantiation() { + // create new instanceEveryTime + doTest { + Json { + isLenient = true + ignoreUnknownKeys = true + useAlternativeNames = true + } + } + } +} diff --git a/formats/json-tests/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt b/formats/json-tests/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt new file mode 100644 index 00000000..58249044 --- /dev/null +++ b/formats/json-tests/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt @@ -0,0 +1,8 @@ +/* + * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.test + + +public actual val currentPlatform: Platform = Platform.NATIVE diff --git a/formats/json-tests/nativeTest/src/kotlinx/serialization/test/JsonHelpers.kt b/formats/json-tests/nativeTest/src/kotlinx/serialization/test/JsonHelpers.kt new file mode 100644 index 00000000..3f98c43d --- /dev/null +++ b/formats/json-tests/nativeTest/src/kotlinx/serialization/test/JsonHelpers.kt @@ -0,0 +1,19 @@ +package kotlinx.serialization.test + +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.json.Json + +actual fun <T> Json.encodeViaStream( + serializer: SerializationStrategy<T>, + value: T +): String { + TODO("supported on JVM only") +} + +actual fun <T> Json.decodeViaStream( + serializer: DeserializationStrategy<T>, + input: String +): T { + TODO("supported on JVM only") +} diff --git a/formats/json-tests/wasmTest/src/kotlinx/serialization/test/CurrentPlatform.kt b/formats/json-tests/wasmTest/src/kotlinx/serialization/test/CurrentPlatform.kt new file mode 100644 index 00000000..fd359b72 --- /dev/null +++ b/formats/json-tests/wasmTest/src/kotlinx/serialization/test/CurrentPlatform.kt @@ -0,0 +1,7 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.test + +public actual val currentPlatform: Platform = Platform.WASM
\ No newline at end of file diff --git a/formats/json-tests/wasmTest/src/kotlinx/serialization/test/JsonHelpers.kt b/formats/json-tests/wasmTest/src/kotlinx/serialization/test/JsonHelpers.kt new file mode 100644 index 00000000..6102e586 --- /dev/null +++ b/formats/json-tests/wasmTest/src/kotlinx/serialization/test/JsonHelpers.kt @@ -0,0 +1,19 @@ +package kotlinx.serialization.test + +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.json.Json + +actual fun <T> Json.encodeViaStream( + serializer: SerializationStrategy<T>, + value: T +): String { + TODO("supported on JVM only") +} + +actual fun <T> Json.decodeViaStream( + serializer: DeserializationStrategy<T>, + input: String +): T { + TODO("supported on JVM only") +}
\ No newline at end of file |