diff --git a/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationStringDecoder.java b/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationStringDecoder.java index 62e23d203e5..13a184f149c 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationStringDecoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationStringDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,8 @@ package org.springframework.http.codec; -import java.util.*; +import java.util.List; +import java.util.Map; import kotlinx.serialization.KSerializer; import kotlinx.serialization.StringFormat; @@ -92,8 +93,7 @@ public abstract class KotlinSerializationStringDecoder e @Override public Flux decode(Publisher inputStream, ResolvableType elementType, - @Nullable MimeType mimeType, - @Nullable Map hints) { + @Nullable MimeType mimeType, @Nullable Map hints) { return Flux.defer(() -> { KSerializer serializer = serializer(elementType); if (serializer == null) { @@ -107,7 +107,7 @@ public abstract class KotlinSerializationStringDecoder e @Override public Mono decodeToMono(Publisher inputStream, ResolvableType elementType, - @Nullable MimeType mimeType, @Nullable Map hints) { + @Nullable MimeType mimeType, @Nullable Map hints) { return Mono.defer(() -> { KSerializer serializer = serializer(elementType); if (serializer == null) { diff --git a/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationStringEncoder.java b/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationStringEncoder.java index 8cdaef51018..803c33d625c 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationStringEncoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationStringEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,16 @@ package org.springframework.http.codec; -import java.util.*; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import kotlin.text.Charsets; import kotlinx.serialization.KSerializer; import kotlinx.serialization.StringFormat; import org.reactivestreams.Publisher; -import org.springframework.http.MediaType; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -32,6 +35,7 @@ import org.springframework.core.codec.Encoder; import org.springframework.core.codec.EncodingException; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.http.MediaType; import org.springframework.lang.Nullable; import org.springframework.util.MimeType; @@ -87,9 +91,10 @@ public abstract class KotlinSerializationStringEncoder e .flux(); } - if (mimeType != null && streamingMediaTypes.contains(mimeType)) { + if (mimeType != null && this.streamingMediaTypes.contains(mimeType)) { return Flux.from(inputStream) - .map(list -> encodeValue(list, bufferFactory, elementType, mimeType, hints).write("\n", Charsets.UTF_8)); + .map(list -> encodeValue(list, bufferFactory, elementType, mimeType, hints) + .write("\n", Charsets.UTF_8)); } ResolvableType listType = ResolvableType.forClassWithGenerics(List.class, elementType); diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/KotlinSerializationJsonEncoder.java b/spring-web/src/main/java/org/springframework/http/codec/json/KotlinSerializationJsonEncoder.java index afa1b5eb41b..897fbaccfd4 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/KotlinSerializationJsonEncoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/KotlinSerializationJsonEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,13 @@ package org.springframework.http.codec.json; +import java.util.List; + import kotlinx.serialization.json.Json; import org.springframework.http.MediaType; import org.springframework.http.codec.KotlinSerializationStringEncoder; -import java.util.List; - /** * Encode from an {@code Object} stream to a byte stream of JSON objects using * kotlinx.serialization. @@ -30,7 +30,7 @@ import java.util.List; *

This encoder can be used to bind {@code @Serializable} Kotlin classes, * open polymorphic serialization * is not supported. - * It supports {@code application/json} and {@code application/*+json} with + * It supports {@code application/json}, {@code application/x-ndjson} and {@code application/*+json} with * various character sets, {@code UTF-8} being the default. * * @author Sebastien Deleuze diff --git a/spring-web/src/test/kotlin/org/springframework/http/codec/json/CustomKotlinSerializationJsonDecoderTests.kt b/spring-web/src/test/kotlin/org/springframework/http/codec/json/CustomKotlinSerializationJsonDecoderTests.kt index 0f5d816eb7b..3354d9f5548 100644 --- a/spring-web/src/test/kotlin/org/springframework/http/codec/json/CustomKotlinSerializationJsonDecoderTests.kt +++ b/spring-web/src/test/kotlin/org/springframework/http/codec/json/CustomKotlinSerializationJsonDecoderTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonDecoderTests.kt b/spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonDecoderTests.kt index ffa4dfc1ea1..6c5fe1b5c5b 100644 --- a/spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonDecoderTests.kt +++ b/spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonDecoderTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,23 +69,44 @@ class KotlinSerializationJsonDecoderTests : AbstractDecoderTests -> - step - .expectNext(Pojo("f1", "b1")) + testDecodeAll(input, ResolvableType.forClass(Pojo::class.java), { + it.expectNext(Pojo("f1", "b1")) + .expectNext(Pojo("f2", "b2")) + .expectComplete() + .verify() + }, null, null) + } + + @Test + fun decodeStreamWithSingleBuffer() { + val input = Flux.concat( + stringBuffer("{\"bar\":\"b1\",\"foo\":\"f1\"}\n{\"bar\":\"b2\",\"foo\":\"f2\"}\n"), + ) + + testDecodeAll(input, ResolvableType.forClass(Pojo::class.java), { + it.expectNext(Pojo("f1", "b1")) + .expectNext(Pojo("f2", "b2")) + .expectComplete() + .verify() + }, null, null) + } + + @Test + fun decodeStreamWithMultipleBuffersPerElement() { + val input = Flux.concat( + stringBuffer("{\"bar\":\"b1\","), + stringBuffer("\"foo\":\"f1\"}\n"), + stringBuffer("{\""), + stringBuffer("bar\":\"b2\",\"foo\":\"f2\"}\n") + ) + + testDecodeAll(input, ResolvableType.forClass(Pojo::class.java), { + it.expectNext(Pojo("f1", "b1")) .expectNext(Pojo("f2", "b2")) .expectComplete() .verify() @@ -100,11 +121,10 @@ class KotlinSerializationJsonDecoderTests : AbstractDecoderTests -> - step - .expectNext(listOf(Pojo("f1", "b1"), Pojo("f2", "b2"))) - .expectComplete() - .verify() + testDecodeToMonoAll(input, elementType, { + it.expectNext(listOf(Pojo("f1", "b1"), Pojo("f2", "b2"))) + .expectComplete() + .verify() }, null, null) } diff --git a/spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonEncoderTests.kt b/spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonEncoderTests.kt index 843eb71d945..8e00528f8c0 100644 --- a/spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonEncoderTests.kt +++ b/spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonEncoderTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,30 +69,30 @@ class KotlinSerializationJsonEncoderTests : AbstractEncoderTests -> step - .consumeNextWith(expectString("[" + - "{\"foo\":\"foo\",\"bar\":\"bar\"}," + - "{\"foo\":\"foofoo\",\"bar\":\"barbar\"}," + - "{\"foo\":\"foofoofoo\",\"bar\":\"barbarbar\"}]") - .andThen { dataBuffer: DataBuffer? -> DataBufferUtils.release(dataBuffer) }) - .verifyComplete() - }) + testEncode(input, Pojo::class.java) { + it.consumeNextWith(expectString("[" + + "{\"foo\":\"foo\",\"bar\":\"bar\"}," + + "{\"foo\":\"foofoo\",\"bar\":\"barbar\"}," + + "{\"foo\":\"foofoofoo\",\"bar\":\"barbarbar\"}]") + .andThen { dataBuffer -> DataBufferUtils.release(dataBuffer) }) + .verifyComplete() + } } + @Test fun encodeStream() { val input = Flux.just( - Pojo("foo", "bar"), - Pojo("foofoo", "barbar"), - Pojo("foofoofoo", "barbarbar") + Pojo("foo", "bar"), + Pojo("foofoo", "barbar"), + Pojo("foofoofoo", "barbarbar") ) testEncodeAll( input, ResolvableType.forClass(Pojo::class.java), MediaType.APPLICATION_NDJSON, null - ) { step: FirstStep -> - step - .consumeNextWith(expectString("{\"foo\":\"foo\",\"bar\":\"bar\"}\n")) + ) { + it.consumeNextWith(expectString("{\"foo\":\"foo\",\"bar\":\"bar\"}\n")) .consumeNextWith(expectString("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}\n")) .consumeNextWith(expectString("{\"foo\":\"foofoofoo\",\"bar\":\"barbarbar\"}\n")) .verifyComplete() @@ -102,11 +102,11 @@ class KotlinSerializationJsonEncoderTests : AbstractEncoderTests -> step - .consumeNextWith(expectString("{\"foo\":\"foo\",\"bar\":\"bar\"}") + testEncode(input, Pojo::class.java) { + it.consumeNextWith(expectString("{\"foo\":\"foo\",\"bar\":\"bar\"}") .andThen { dataBuffer: DataBuffer? -> DataBufferUtils.release(dataBuffer) }) .verifyComplete() - }) + } } @Test