Polishing

See gh-32074
This commit is contained in:
Sébastien Deleuze 2024-01-24 15:18:28 +01:00
parent 11898daed7
commit 4f16297e45
6 changed files with 76 additions and 51 deletions

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,7 +16,8 @@
package org.springframework.http.codec; package org.springframework.http.codec;
import java.util.*; import java.util.List;
import java.util.Map;
import kotlinx.serialization.KSerializer; import kotlinx.serialization.KSerializer;
import kotlinx.serialization.StringFormat; import kotlinx.serialization.StringFormat;
@ -92,8 +93,7 @@ public abstract class KotlinSerializationStringDecoder<T extends StringFormat> e
@Override @Override
public Flux<Object> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType, public Flux<Object> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
@Nullable Map<String, Object> hints) {
return Flux.defer(() -> { return Flux.defer(() -> {
KSerializer<Object> serializer = serializer(elementType); KSerializer<Object> serializer = serializer(elementType);
if (serializer == null) { if (serializer == null) {

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,13 +16,16 @@
package org.springframework.http.codec; 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 kotlin.text.Charsets;
import kotlinx.serialization.KSerializer; import kotlinx.serialization.KSerializer;
import kotlinx.serialization.StringFormat; import kotlinx.serialization.StringFormat;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import org.springframework.http.MediaType;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; 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.codec.EncodingException;
import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.MimeType; import org.springframework.util.MimeType;
@ -87,9 +91,10 @@ public abstract class KotlinSerializationStringEncoder<T extends StringFormat> e
.flux(); .flux();
} }
if (mimeType != null && streamingMediaTypes.contains(mimeType)) { if (mimeType != null && this.streamingMediaTypes.contains(mimeType)) {
return Flux.from(inputStream) 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); ResolvableType listType = ResolvableType.forClassWithGenerics(List.class, elementType);

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,13 +16,13 @@
package org.springframework.http.codec.json; package org.springframework.http.codec.json;
import java.util.List;
import kotlinx.serialization.json.Json; import kotlinx.serialization.json.Json;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.codec.KotlinSerializationStringEncoder; import org.springframework.http.codec.KotlinSerializationStringEncoder;
import java.util.List;
/** /**
* Encode from an {@code Object} stream to a byte stream of JSON objects using * Encode from an {@code Object} stream to a byte stream of JSON objects using
* <a href="https://github.com/Kotlin/kotlinx.serialization">kotlinx.serialization</a>. * <a href="https://github.com/Kotlin/kotlinx.serialization">kotlinx.serialization</a>.
@ -30,7 +30,7 @@ import java.util.List;
* <p>This encoder can be used to bind {@code @Serializable} Kotlin classes, * <p>This encoder can be used to bind {@code @Serializable} Kotlin classes,
* <a href="https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#open-polymorphism">open polymorphic serialization</a> * <a href="https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#open-polymorphism">open polymorphic serialization</a>
* is not supported. * 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. * various character sets, {@code UTF-8} being the default.
* *
* @author Sebastien Deleuze * @author Sebastien Deleuze

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -69,23 +69,44 @@ class KotlinSerializationJsonDecoderTests : AbstractDecoderTests<KotlinSerializa
@Test @Test
override fun decode() { override fun decode() {
val output = decoder.decode(Mono.empty(), ResolvableType.forClass(Pojo::class.java), null, emptyMap())
StepVerifier
.create(output)
.expectError(UnsupportedOperationException::class.java)
}
@Test
fun decodeStream() {
val input = Flux.concat( val input = Flux.concat(
stringBuffer("{\"bar\":\"b1\",\"foo\":\"f1\"}\n"), stringBuffer("{\"bar\":\"b1\",\"foo\":\"f1\"}\n"),
stringBuffer("{\"bar\":\"b2\",\"foo\":\"f2\"}\n") stringBuffer("{\"bar\":\"b2\",\"foo\":\"f2\"}\n")
) )
testDecodeAll(input, ResolvableType.forClass(Pojo::class.java), { step: FirstStep<Any> -> testDecodeAll(input, ResolvableType.forClass(Pojo::class.java), {
step it.expectNext(Pojo("f1", "b1"))
.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")) .expectNext(Pojo("f2", "b2"))
.expectComplete() .expectComplete()
.verify() .verify()
@ -100,9 +121,8 @@ class KotlinSerializationJsonDecoderTests : AbstractDecoderTests<KotlinSerializa
val elementType = ResolvableType.forClassWithGenerics(List::class.java, Pojo::class.java) val elementType = ResolvableType.forClassWithGenerics(List::class.java, Pojo::class.java)
testDecodeToMonoAll(input, elementType, { step: FirstStep<Any> -> testDecodeToMonoAll(input, elementType, {
step it.expectNext(listOf(Pojo("f1", "b1"), Pojo("f2", "b2")))
.expectNext(listOf(Pojo("f1", "b1"), Pojo("f2", "b2")))
.expectComplete() .expectComplete()
.verify() .verify()
}, null, null) }, null, null)

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -69,15 +69,16 @@ class KotlinSerializationJsonEncoderTests : AbstractEncoderTests<KotlinSerializa
Pojo("foofoo", "barbar"), Pojo("foofoo", "barbar"),
Pojo("foofoofoo", "barbarbar") Pojo("foofoofoo", "barbarbar")
) )
testEncode(input, Pojo::class.java, { step: FirstStep<DataBuffer?> -> step testEncode(input, Pojo::class.java) {
.consumeNextWith(expectString("[" + it.consumeNextWith(expectString("[" +
"{\"foo\":\"foo\",\"bar\":\"bar\"}," + "{\"foo\":\"foo\",\"bar\":\"bar\"}," +
"{\"foo\":\"foofoo\",\"bar\":\"barbar\"}," + "{\"foo\":\"foofoo\",\"bar\":\"barbar\"}," +
"{\"foo\":\"foofoofoo\",\"bar\":\"barbarbar\"}]") "{\"foo\":\"foofoofoo\",\"bar\":\"barbarbar\"}]")
.andThen { dataBuffer: DataBuffer? -> DataBufferUtils.release(dataBuffer) }) .andThen { dataBuffer -> DataBufferUtils.release(dataBuffer) })
.verifyComplete() .verifyComplete()
})
} }
}
@Test @Test
fun encodeStream() { fun encodeStream() {
val input = Flux.just( val input = Flux.just(
@ -90,9 +91,8 @@ class KotlinSerializationJsonEncoderTests : AbstractEncoderTests<KotlinSerializa
ResolvableType.forClass(Pojo::class.java), ResolvableType.forClass(Pojo::class.java),
MediaType.APPLICATION_NDJSON, MediaType.APPLICATION_NDJSON,
null null
) { step: FirstStep<DataBuffer?> -> ) {
step it.consumeNextWith(expectString("{\"foo\":\"foo\",\"bar\":\"bar\"}\n"))
.consumeNextWith(expectString("{\"foo\":\"foo\",\"bar\":\"bar\"}\n"))
.consumeNextWith(expectString("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}\n")) .consumeNextWith(expectString("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}\n"))
.consumeNextWith(expectString("{\"foo\":\"foofoofoo\",\"bar\":\"barbarbar\"}\n")) .consumeNextWith(expectString("{\"foo\":\"foofoofoo\",\"bar\":\"barbarbar\"}\n"))
.verifyComplete() .verifyComplete()
@ -102,11 +102,11 @@ class KotlinSerializationJsonEncoderTests : AbstractEncoderTests<KotlinSerializa
@Test @Test
fun encodeMono() { fun encodeMono() {
val input = Mono.just(Pojo("foo", "bar")) val input = Mono.just(Pojo("foo", "bar"))
testEncode(input, Pojo::class.java, { step: FirstStep<DataBuffer?> -> step testEncode(input, Pojo::class.java) {
.consumeNextWith(expectString("{\"foo\":\"foo\",\"bar\":\"bar\"}") it.consumeNextWith(expectString("{\"foo\":\"foo\",\"bar\":\"bar\"}")
.andThen { dataBuffer: DataBuffer? -> DataBufferUtils.release(dataBuffer) }) .andThen { dataBuffer: DataBuffer? -> DataBufferUtils.release(dataBuffer) })
.verifyComplete() .verifyComplete()
}) }
} }
@Test @Test