diff options
Diffstat (limited to 'formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonIterator.kt')
-rw-r--r-- | formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonIterator.kt | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonIterator.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonIterator.kt new file mode 100644 index 00000000..00f36b2b --- /dev/null +++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonIterator.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:Suppress("FunctionName") +@file:OptIn(ExperimentalSerializationApi::class) + +package kotlinx.serialization.json.internal + +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.* + +internal fun <T> JsonIterator( + mode: DecodeSequenceMode, + json: Json, + lexer: ReaderJsonLexer, + deserializer: DeserializationStrategy<T> +): Iterator<T> = when (lexer.determineFormat(mode)) { + DecodeSequenceMode.WHITESPACE_SEPARATED -> JsonIteratorWsSeparated( + json, + lexer, + deserializer + ) // Can be many WS-separated independent arrays + DecodeSequenceMode.ARRAY_WRAPPED -> JsonIteratorArrayWrapped( + json, + lexer, + deserializer + ) + DecodeSequenceMode.AUTO_DETECT -> error("AbstractJsonLexer.determineFormat must be called beforehand.") +} + + +private fun AbstractJsonLexer.determineFormat(suggested: DecodeSequenceMode): DecodeSequenceMode = when (suggested) { + DecodeSequenceMode.WHITESPACE_SEPARATED -> + DecodeSequenceMode.WHITESPACE_SEPARATED // do not call consumeStartArray here so we don't confuse parser with stream of lists + DecodeSequenceMode.ARRAY_WRAPPED -> + if (tryConsumeStartArray()) DecodeSequenceMode.ARRAY_WRAPPED + else fail(TC_BEGIN_LIST) + DecodeSequenceMode.AUTO_DETECT -> + if (tryConsumeStartArray()) DecodeSequenceMode.ARRAY_WRAPPED + else DecodeSequenceMode.WHITESPACE_SEPARATED +} + +private fun AbstractJsonLexer.tryConsumeStartArray(): Boolean { + if (peekNextToken() == TC_BEGIN_LIST) { + consumeNextToken(TC_BEGIN_LIST) + return true + } + return false +} + +private class JsonIteratorWsSeparated<T>( + private val json: Json, + private val lexer: ReaderJsonLexer, + private val deserializer: DeserializationStrategy<T> +) : Iterator<T> { + override fun next(): T = + StreamingJsonDecoder(json, WriteMode.OBJ, lexer, deserializer.descriptor, null) + .decodeSerializableValue(deserializer) + + override fun hasNext(): Boolean = lexer.isNotEof() +} + +private class JsonIteratorArrayWrapped<T>( + private val json: Json, + private val lexer: ReaderJsonLexer, + private val deserializer: DeserializationStrategy<T> +) : Iterator<T> { + private var first = true + private var finished = false + + override fun next(): T { + if (first) { + first = false + } else { + lexer.consumeNextToken(COMMA) + } + val input = StreamingJsonDecoder(json, WriteMode.OBJ, lexer, deserializer.descriptor, null) + return input.decodeSerializableValue(deserializer) + } + + /** + * Note: if array separator (comma) is missing, hasNext() returns true, but next() throws an exception. + */ + override fun hasNext(): Boolean { + if (finished) return false + if (lexer.peekNextToken() == TC_END_LIST) { + finished = true + lexer.consumeNextToken(TC_END_LIST) + if (lexer.isNotEof()) { + if (lexer.peekNextToken() == TC_BEGIN_LIST) lexer.fail("There is a start of the new array after the one parsed to sequence. " + + "${DecodeSequenceMode.ARRAY_WRAPPED.name} mode doesn't merge consecutive arrays.\n" + + "If you need to parse a stream of arrays, please use ${DecodeSequenceMode.WHITESPACE_SEPARATED.name} mode instead.") + lexer.expectEof() + } + return false + } + if (!lexer.isNotEof() && !finished) lexer.fail(TC_END_LIST) + return true + } +} |