Skip to content

Commit 350443a

Browse files
authored
Fix incorrect json decoding iterator's .hasNext() behavior on array-wrapped inputs: (#2268)
hasNext() shouldn't throw exception if it was called more than once after the stream has ended. Fixes #2267
1 parent bbf248e commit 350443a

File tree

4 files changed

+16
-2
lines changed

4 files changed

+16
-2
lines changed

formats/json-tests/jvmTest/src/kotlinx/serialization/features/JsonLazySequenceTest.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class JsonLazySequenceTest {
8383
iter.assertNext(StringData("b"))
8484
iter.assertNext(StringData("c"))
8585
assertFalse(iter.hasNext())
86+
assertFalse(iter.hasNext()) // Subsequent calls to .hasNext() should not throw EOF or anything
8687
assertFailsWithMessage<SerializationException>("EOF") {
8788
iter.next()
8889
}
@@ -186,4 +187,11 @@ class JsonLazySequenceTest {
186187
assertEquals(inputList, json.decodeToSequence(paddedWs.asInputStream(), StringData.serializer(), DecodeSequenceMode.ARRAY_WRAPPED).toList())
187188
}
188189

190+
@Test
191+
fun testToIteratorAndBack() = withInputs { ins ->
192+
val iterator = Json.decodeToSequence(ins, StringData.serializer()).iterator()
193+
val values = iterator.asSequence().take(3).toList()
194+
assertEquals(inputList, values)
195+
assertFalse(iterator.hasNext())
196+
}
189197
}

formats/json/commonMain/src/kotlinx/serialization/json/Json.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ public enum class DecodeSequenceMode {
169169

170170
/**
171171
* Declares that parser itself should select between [WHITESPACE_SEPARATED] and [ARRAY_WRAPPED] modes.
172-
* The selection is performed by looking on the first meaningful character of the stream.
172+
* The selection is performed by looking at the first meaningful character of the stream.
173173
*
174174
* In most cases, auto-detection is sufficient to correctly parse an input.
175175
* If the input is _whitespace-separated stream of the arrays_, parser could select an incorrect mode,

formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonIterator.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ private class JsonIteratorArrayWrapped<T>(
6868
private val deserializer: DeserializationStrategy<T>
6969
) : Iterator<T> {
7070
private var first = true
71+
private var finished = false
7172

7273
override fun next(): T {
7374
if (first) {
@@ -83,7 +84,9 @@ private class JsonIteratorArrayWrapped<T>(
8384
* Note: if array separator (comma) is missing, hasNext() returns true, but next() throws an exception.
8485
*/
8586
override fun hasNext(): Boolean {
87+
if (finished) return false
8688
if (lexer.peekNextToken() == TC_END_LIST) {
89+
finished = true
8790
lexer.consumeNextToken(TC_END_LIST)
8891
if (lexer.isNotEof()) {
8992
if (lexer.peekNextToken() == TC_BEGIN_LIST) lexer.fail("There is a start of the new array after the one parsed to sequence. " +
@@ -93,7 +96,7 @@ private class JsonIteratorArrayWrapped<T>(
9396
}
9497
return false
9598
}
96-
if (!lexer.isNotEof()) lexer.fail(TC_END_LIST)
99+
if (!lexer.isNotEof() && !finished) lexer.fail(TC_END_LIST)
97100
return true
98101
}
99102
}

formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/JsonLexer.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ internal class ArrayAsSequence(private val buffer: CharArray) : CharSequence {
3030
fun trim(newSize: Int) {
3131
length = minOf(buffer.size, newSize)
3232
}
33+
34+
// source.toString() is used in JsonDecodingException
35+
override fun toString(): String = substring(0, length)
3336
}
3437

3538
internal class ReaderJsonLexer(

0 commit comments

Comments
 (0)