• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.serialization
6 
7 import kotlinx.serialization.json.*
8 import kotlinx.serialization.test.*
9 import kotlin.test.*
10 
11 class JsonPathTest : JsonTestBase() {
12 
13     @Serializable
14     class Outer(val a: Int, val i: Inner)
15 
16     @Serializable
17     class Inner(val a: Int, val b: String, val c: List<String>, val d: Map<Int, Box>)
18 
19     @Serializable
20     class Box(val s: String)
21 
22     @Test
testBasicErrornull23     fun testBasicError() {
24         expectPath("$.a") { Json.decodeFromString<Outer>("""{"a":foo}""") }
25         expectPath("$.i") { Json.decodeFromString<Outer>("""{"a":42, "i":[]}""") }
26         expectPath("$.i.b") { Json.decodeFromString<Outer>("""{"a":42, "i":{"a":43, "b":42}""") }
27         expectPath("$.i.b") { Json.decodeFromString<Outer>("""{"a":42, "i":{"b":42}""") }
28     }
29 
30     @Test
testMissingKeynull31     fun testMissingKey() {
32         expectPath("$.i.d['1']") { Json.decodeFromString<Outer>("""{"a":42, "i":{"d":{1:{}}""") }
33     }
34 
35     @Test
testUnknownKeyIsProperlyReportednull36     fun testUnknownKeyIsProperlyReported() {
37         expectPath("$.i") { Json.decodeFromString<Outer>("""{"a":42, "i":{"foo":42}""") }
38         expectPath("$") { Json.decodeFromString<Outer>("""{"x":{}, "a": 42}""") }
39         // The only place we have misattribution in
40         // Json.decodeFromString<Outer>("""{"a":42, "x":{}}""")
41     }
42 
43     @Test
testMalformedRootObjectnull44     fun testMalformedRootObject() {
45         expectPath("$") { Json.decodeFromString<Outer>("""{{""") }
46     }
47 
48     @Test
testArrayIndexnull49     fun testArrayIndex() {
50         expectPath("$.i.c[1]") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "c": ["a", 2]}""") }
51         expectPath("$[2]") { Json.decodeFromString<List<String>>("""["a", "2", 3]""") }
52     }
53 
54     @Test
testArrayIndexMalformedArraynull55     fun testArrayIndexMalformedArray() {
56         // Also zeroes as we cannot distinguish what exactly wen wrong is such cases
57         expectPath("$.i.c[0]") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "c": [[""") }
58         expectPath("$[0]") { Json.decodeFromString<List<String>>("""[[""") }
59         // But we can here
60         expectPath("$.i.c\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "c": {}}}""") }
61         expectPath("$\n") { Json.decodeFromString<List<String>>("""{""") }
62     }
63 
64     @Test
testMapKeynull65     fun testMapKey() {
66         expectPath("$.i.d\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "d": {"foo": {}}""") }
67         expectPath("$.i.d\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "d": {42: {"s":"s"}, 42.0:{}}""") }
68         expectPath("$\n") { Json.decodeFromString<Map<Int, String>>("""{"foo":"bar"}""") }
69         expectPath("$\n") { Json.decodeFromString<Map<Int, String>>("""{42:"bar", "foo":"bar"}""") }
70         expectPath("$['42']['foo']") { Json.decodeFromString<Map<Int, Map<String, Int>>>("""{42: {"foo":"bar"}""") }
71     }
72 
73     @Test
testMalformedMapnull74     fun testMalformedMap() {
75         expectPath("$.i.d\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "d": []""") }
76         expectPath("$\n") { Json.decodeFromString<Map<Int, String>>("""[]""") }
77     }
78 
79     @Test
testMapValuenull80     fun testMapValue() {
81         expectPath("$.i.d['42']\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "d": {42: {"xx":"bar"}}""") }
82         expectPath("$.i.d['43']\n") { Json.decodeFromString<Outer>("""{"a":42, "i":{ "d": {42: {"s":"s"}, 43: {"xx":"bar"}}}""") }
83         expectPath("$['239']") { Json.decodeFromString<Map<Int, String>>("""{239:bar}""") }
84     }
85 
86     @Serializable
87     class Fp(val d: Double)
88 
89     @Test
testInvalidFpnull90     fun testInvalidFp() {
91         expectPath("$.d") { Json.decodeFromString<Fp>("""{"d": NaN}""") }
92     }
93 
94     @Serializable
95     class EH(val e: E)
96     enum class E
97 
98     @Test
testUnknownEnumnull99     fun testUnknownEnum() {
100         expectPath("$.e") { Json.decodeFromString<EH>("""{"e": "foo"}""") }
101     }
102 
103     @Serializable
104     @SerialName("f")
105     sealed class Sealed {
106 
107         @Serializable
108         @SerialName("n")
109         class Nesting(val f: Sealed) : Sealed()
110 
111         @Serializable
112         @SerialName("b")
113         class Box(val s: String) : Sealed()
114 
115         @Serializable
116         @SerialName("d")
117         class DoubleNesting(val f: Sealed, val f2: Sealed) : Sealed()
118     }
119 
120     // TODO use non-array polymorphism when https://github.com/Kotlin/kotlinx.serialization/issues/1839 is fixed
121     @Test
<lambda>null122     fun testHugeNestingToCheckResize() = jvmOnly {
123         val json = Json { useArrayPolymorphism = true }
124         var outer = Sealed.Nesting(Sealed.Box("value"))
125         repeat(100) {
126             outer = Sealed.Nesting(outer)
127         }
128         val str = json.encodeToString(Sealed.serializer(), outer)
129         // throw-away data
130         json.decodeFromString(Sealed.serializer(), str)
131 
132         val malformed = str.replace("\"value\"", "42")
133         val expectedPath = "$" + ".value.f".repeat(101) + ".value.s"
134         expectPath(expectedPath) { json.decodeFromString(Sealed.serializer(), malformed) }
135     }
136 
137     @Test
<lambda>null138     fun testDoubleNesting() = jvmOnly {
139         val json = Json { useArrayPolymorphism = true }
140         var outer1 = Sealed.Nesting(Sealed.Box("correct"))
141         repeat(64) {
142             outer1 = Sealed.Nesting(outer1)
143         }
144 
145         var outer2 = Sealed.Nesting(Sealed.Box("incorrect"))
146         repeat(33) {
147             outer2 = Sealed.Nesting(outer2)
148         }
149 
150         val str = json.encodeToString(Sealed.serializer(), Sealed.DoubleNesting(outer1, outer2))
151         // throw-away data
152         json.decodeFromString(Sealed.serializer(), str)
153 
154         val malformed = str.replace("\"incorrect\"", "42")
155         val expectedPath = "$.value.f2" + ".value.f".repeat(34) + ".value.s"
156         expectPath(expectedPath) { json.decodeFromString(Sealed.serializer(), malformed) }
157     }
158 
expectPathnull159     private inline fun expectPath(path: String, block: () -> Unit) {
160         val message = runCatching { block() }
161             .exceptionOrNull()!!.message!!
162         assertContains(message, path)
163     }
164 }
165