diff --git a/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationBinaryDecoder.java b/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationBinaryDecoder.java index 46d5e9f75b4..c123629434b 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationBinaryDecoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationBinaryDecoder.java @@ -18,6 +18,7 @@ package org.springframework.http.codec; import java.util.List; import java.util.Map; +import java.util.function.Predicate; import kotlinx.serialization.BinaryFormat; import kotlinx.serialization.KSerializer; @@ -37,9 +38,11 @@ import org.springframework.util.MimeType; * Abstract base class for {@link Decoder} implementations that defer to Kotlin * {@linkplain BinaryFormat binary serializers}. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only decodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics level + * since it allows combined usage with other general purpose decoders without conflicts. + * Alternative constructors with a {@code Predicate} parameter can be used + * to customize this behavior. * * @author Sebastien Deleuze * @author Iain Henderson @@ -54,10 +57,26 @@ public abstract class KotlinSerializationBinaryDecoder e private final ByteArrayDecoder byteArrayDecoder = new ByteArrayDecoder(); + /** + * Creates a new instance with the given format and supported mime types + * which only decodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or + * generics level. + */ public KotlinSerializationBinaryDecoder(T format, MimeType... supportedMimeTypes) { super(format, supportedMimeTypes); } + /** + * Creates a new instance with the given format and supported mime types + * which only decodes types for which the specified predicate returns + * {@code true}. + * @since 7.0 + */ + public KotlinSerializationBinaryDecoder(T format, Predicate typePredicate, MimeType... supportedMimeTypes) { + super(format, typePredicate, supportedMimeTypes); + } + /** * Configure a limit on the number of bytes that can be buffered whenever * the input stream needs to be aggregated. This can be a result of diff --git a/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationBinaryEncoder.java b/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationBinaryEncoder.java index 3a3170b0e98..3b5f18d979d 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationBinaryEncoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationBinaryEncoder.java @@ -18,6 +18,7 @@ package org.springframework.http.codec; import java.util.List; import java.util.Map; +import java.util.function.Predicate; import kotlinx.serialization.BinaryFormat; import kotlinx.serialization.KSerializer; @@ -38,9 +39,11 @@ import org.springframework.util.MimeType; * Abstract base class for {@link Encoder} implementations that defer to Kotlin * {@linkplain BinaryFormat binary serializers}. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only encodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics level + * since it allows combined usage with other general purpose encoders without conflicts. + * Alternative constructors with a {@code Predicate} parameter can be used + * to customize this behavior. * * @author Sebastien Deleuze * @author Iain Henderson @@ -54,10 +57,26 @@ public abstract class KotlinSerializationBinaryEncoder e // ByteArraySequence encoding needed for now, see https://github.com/Kotlin/kotlinx.serialization/issues/204 for more details private final ByteArrayEncoder byteArrayEncoder = new ByteArrayEncoder(); + /** + * Creates a new instance with the given format and supported mime types + * which only encodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or + * generics level. + */ protected KotlinSerializationBinaryEncoder(T format, MimeType... supportedMimeTypes) { super(format, supportedMimeTypes); } + /** + * Creates a new instance with the given format and supported mime types + * which only encodes types for which the specified predicate returns + * {@code true}. + * @since 7.0 + */ + protected KotlinSerializationBinaryEncoder(T format, Predicate typePredicate, MimeType... supportedMimeTypes) { + super(format, typePredicate, supportedMimeTypes); + } + @Override public boolean canEncode(ResolvableType elementType, @Nullable MimeType mimeType) { return canSerialize(elementType, mimeType); 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 423aa4087cf..684df592298 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 @@ -18,6 +18,7 @@ package org.springframework.http.codec; import java.util.List; import java.util.Map; +import java.util.function.Predicate; import kotlinx.serialization.KSerializer; import kotlinx.serialization.StringFormat; @@ -38,9 +39,11 @@ import org.springframework.util.MimeType; * Abstract base class for {@link Decoder} implementations that defer to Kotlin * {@linkplain StringFormat string serializers}. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only decodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics level + * since it allows combined usage with other general purpose decoders without conflicts. + * Alternative constructors with a {@code Predicate} parameter can be used + * to customize this behavior. * * @author Sebastien Deleuze * @author Iain Henderson @@ -55,10 +58,26 @@ public abstract class KotlinSerializationStringDecoder e private final StringDecoder stringDecoder = StringDecoder.allMimeTypes(StringDecoder.DEFAULT_DELIMITERS, false); + /** + * Creates a new instance with the given format and supported mime types + * which only decodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or + * generics level. + */ public KotlinSerializationStringDecoder(T format, MimeType... supportedMimeTypes) { super(format, supportedMimeTypes); } + /** + * Creates a new instance with the given format and supported mime types + * which only decodes types for which the specified predicate returns + * {@code true}. + * @since 7.0 + */ + public KotlinSerializationStringDecoder(T format, Predicate typePredicate, MimeType... supportedMimeTypes) { + super(format, typePredicate, supportedMimeTypes); + } + /** * Configure a limit on the number of bytes that can be buffered whenever * the input stream needs to be aggregated. This can be a result of 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 b150ada67cc..18d7330b453 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 @@ -22,6 +22,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Predicate; import kotlinx.serialization.KSerializer; import kotlinx.serialization.StringFormat; @@ -43,9 +44,11 @@ import org.springframework.util.MimeType; * Abstract base class for {@link Encoder} implementations that defer to Kotlin * {@linkplain StringFormat string serializers}. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only encodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics level + * since it allows combined usage with other general purpose encoders without conflicts. + * Alternative constructors with a {@code Predicate} parameter can be used + * to customize this behavior. * * @author Sebastien Deleuze * @author Iain Henderson @@ -65,10 +68,27 @@ public abstract class KotlinSerializationStringEncoder e private final CharSequenceEncoder charSequenceEncoder = CharSequenceEncoder.allMimeTypes(); private final Set streamingMediaTypes = new HashSet<>(); + + /** + * Creates a new instance with the given format and supported mime types + * which only encodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or + * generics level. + */ protected KotlinSerializationStringEncoder(T format, MimeType... supportedMimeTypes) { super(format, supportedMimeTypes); } + /** + * Creates a new instance with the given format and supported mime types + * which only encodes types for which the specified predicate returns + * {@code true}. + * @since 7.0 + */ + protected KotlinSerializationStringEncoder(T format, Predicate typePredicate, MimeType... supportedMimeTypes) { + super(format, typePredicate, supportedMimeTypes); + } + /** * Set streaming {@link MediaType MediaTypes}. * @param streamingMediaTypes streaming {@link MediaType MediaTypes} diff --git a/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationSupport.java b/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationSupport.java index 7744911da0e..1d144926c0a 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationSupport.java +++ b/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationSupport.java @@ -21,6 +21,7 @@ import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.function.Predicate; import kotlin.reflect.KFunction; import kotlin.reflect.KType; @@ -42,9 +43,11 @@ import org.springframework.util.MimeType; * Base class providing support methods for encoding and decoding with Kotlin * serialization. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only handles types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics level + * since it allows combined usage with other general purpose decoders without conflicts. + * Alternative constructors with a {@code Predicate} parameter can be used + * to customize this behavior. * * @author Sebastien Deleuze * @author Iain Henderson @@ -63,12 +66,29 @@ public abstract class KotlinSerializationSupport { private final List supportedMimeTypes; + private final Predicate typePredicate; + /** - * Creates a new instance of this support class with the given format - * and supported mime types. + * Creates a new instance with the given format and supported mime types + * which only handle types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or + * generics level. */ protected KotlinSerializationSupport(T format, MimeType... supportedMimeTypes) { this.format = format; + this.typePredicate = KotlinDetector::hasSerializableAnnotation; + this.supportedMimeTypes = Arrays.asList(supportedMimeTypes); + } + + /** + * Creates a new instance with the given format and supported mime types + * which only encode types for which the specified predicate returns + * {@code true}. + * @since 7.0 + */ + protected KotlinSerializationSupport(T format, Predicate typePredicate, MimeType... supportedMimeTypes) { + this.format = format; + this.typePredicate = typePredicate; this.supportedMimeTypes = Arrays.asList(supportedMimeTypes); } @@ -94,15 +114,10 @@ public abstract class KotlinSerializationSupport { * @return {@code true} if {@code type} can be serialized; false otherwise */ protected final boolean canSerialize(ResolvableType type, @Nullable MimeType mimeType) { - KSerializer serializer = serializer(type); - if (serializer == null) { + if (!this.typePredicate.test(type) || ResolvableType.NONE.equals(type)) { return false; } - else { - return (supports(mimeType) && !String.class.isAssignableFrom(type.toClass()) && - !ServerSentEvent.class.isAssignableFrom(type.toClass())); - } - + return serializer(type) != null && supports(mimeType); } private boolean supports(@Nullable MimeType mimeType) { diff --git a/spring-web/src/main/java/org/springframework/http/codec/cbor/KotlinSerializationCborDecoder.java b/spring-web/src/main/java/org/springframework/http/codec/cbor/KotlinSerializationCborDecoder.java index f89566e2eb7..15c29eeb80d 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/cbor/KotlinSerializationCborDecoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/cbor/KotlinSerializationCborDecoder.java @@ -16,8 +16,11 @@ package org.springframework.http.codec.cbor; +import java.util.function.Predicate; + import kotlinx.serialization.cbor.Cbor; +import org.springframework.core.ResolvableType; import org.springframework.http.MediaType; import org.springframework.http.codec.KotlinSerializationBinaryDecoder; @@ -26,24 +29,62 @@ import org.springframework.http.codec.KotlinSerializationBinaryDecoder; * kotlinx.serialization. * It supports {@code application/cbor}. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only decodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics + * level since it allows combined usage with other general purpose CBOR decoders + * like {@link JacksonCborDecoder} without conflicts. + * + *

Alternative constructors with a {@code Predicate} + * parameter can be used to customize this behavior. For example, + * {@code new KotlinSerializationCborDecoder(type -> true)} will decode all types + * supported by Kotlin Serialization, including unannotated Kotlin enumerations, + * numbers, characters, booleans and strings. * *

Decoding streams is not supported yet, see * kotlinx.serialization/issues/1073 * related issue. * * @author Iain Henderson + * @author Sebastien Deleuze * @since 6.0 + * @see KotlinSerializationCborEncoder */ public class KotlinSerializationCborDecoder extends KotlinSerializationBinaryDecoder { + /** + * Construct a new decoder using {@link Cbor.Default} instance which + * only decodes types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ public KotlinSerializationCborDecoder() { this(Cbor.Default); } + /** + * Construct a new decoder using {@link Cbor.Default} instance which + * only decodes types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationCborDecoder(Predicate typePredicate) { + this(Cbor.Default, typePredicate); + } + + /** + * Construct a new decoder using the provided {@link Cbor} instance which + * only decodes types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ public KotlinSerializationCborDecoder(Cbor cbor) { super(cbor, MediaType.APPLICATION_CBOR); } + + /** + * Construct a new decoder using the provided {@link Cbor} instance which + * only decodes types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationCborDecoder(Cbor cbor, Predicate typePredicate) { + super(cbor, typePredicate, MediaType.APPLICATION_CBOR); + } + } diff --git a/spring-web/src/main/java/org/springframework/http/codec/cbor/KotlinSerializationCborEncoder.java b/spring-web/src/main/java/org/springframework/http/codec/cbor/KotlinSerializationCborEncoder.java index d492c9172a3..9fd8c8dee66 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/cbor/KotlinSerializationCborEncoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/cbor/KotlinSerializationCborEncoder.java @@ -16,8 +16,11 @@ package org.springframework.http.codec.cbor; +import java.util.function.Predicate; + import kotlinx.serialization.cbor.Cbor; +import org.springframework.core.ResolvableType; import org.springframework.http.MediaType; import org.springframework.http.codec.KotlinSerializationBinaryEncoder; @@ -26,21 +29,58 @@ import org.springframework.http.codec.KotlinSerializationBinaryEncoder; * kotlinx.serialization. * It supports {@code application/cbor}. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only encodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics + * level since it allows combined usage with other general purpose JSON encoders + * like {@link JacksonCborEncoder} without conflicts. + * + *

Alternative constructors with a {@code Predicate} + * parameter can be used to customize this behavior. For example, + * {@code new KotlinSerializationCborEncoder(type -> true)} will encode all types + * supported by Kotlin Serialization, including unannotated Kotlin enumerations, + * numbers, characters, booleans and strings. * * @author Iain Henderson + * @author Sebastien Deleuze * @since 6.0 + * @see KotlinSerializationCborDecoder */ public class KotlinSerializationCborEncoder extends KotlinSerializationBinaryEncoder { + /** + * Construct a new encoder using {@link Cbor.Default} instance which + * only encodes types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ public KotlinSerializationCborEncoder() { this(Cbor.Default); } + /** + * Construct a new encoder using {@link Cbor.Default} instance which + * only encodes types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationCborEncoder(Predicate typePredicate) { + this(Cbor.Default, typePredicate); + } + + /** + * Construct a new encoder using the provided {@link Cbor} instance which + * only encodes types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ public KotlinSerializationCborEncoder(Cbor cbor) { super(cbor, MediaType.APPLICATION_CBOR); } + /** + * Construct a new encoder using the provided {@link Cbor} instance which + * only encodes types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationCborEncoder(Cbor cbor, Predicate typePredicate) { + super(cbor, typePredicate, MediaType.APPLICATION_CBOR); + } + } diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/KotlinSerializationJsonDecoder.java b/spring-web/src/main/java/org/springframework/http/codec/json/KotlinSerializationJsonDecoder.java index d7d7d673226..227f320987c 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/KotlinSerializationJsonDecoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/KotlinSerializationJsonDecoder.java @@ -16,10 +16,14 @@ package org.springframework.http.codec.json; +import java.util.function.Predicate; + import kotlinx.serialization.json.Json; +import org.springframework.core.ResolvableType; import org.springframework.http.MediaType; import org.springframework.http.codec.KotlinSerializationStringDecoder; +import org.springframework.util.MimeType; /** * Decode a byte stream into JSON and convert to Object's with @@ -27,9 +31,16 @@ import org.springframework.http.codec.KotlinSerializationStringDecoder; * It supports {@code application/json} and {@code application/*+json} with * various character sets, {@code UTF-8} being the default. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only decodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics + * level since it allows combined usage with other general purpose JSON decoders + * like {@link JacksonJsonDecoder} without conflicts. + * + *

Alternative constructors with a {@code Predicate} + * parameter can be used to customize this behavior. For example, + * {@code new KotlinSerializationJsonDecoder(type -> true)} will decode all types + * supported by Kotlin Serialization, including unannotated Kotlin enumerations, + * numbers, characters, booleans and strings. * *

Decoding streams is not supported yet, see * kotlinx.serialization/issues/1073 @@ -38,16 +49,50 @@ import org.springframework.http.codec.KotlinSerializationStringDecoder; * @author Sebastien Deleuze * @author Iain Henderson * @since 5.3 + * @see KotlinSerializationJsonEncoder */ public class KotlinSerializationJsonDecoder extends KotlinSerializationStringDecoder { + private static final MimeType[] DEFAULT_JSON_MIME_TYPES = new MimeType[] { + MediaType.APPLICATION_JSON, + new MediaType("application", "*+json"), + MediaType.APPLICATION_NDJSON + }; + + /** + * Construct a new decoder using {@link Json.Default} instance which + * only decodes types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ public KotlinSerializationJsonDecoder() { this(Json.Default); } + /** + * Construct a new decoder using {@link Json.Default} instance which + * only decodes types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationJsonDecoder(Predicate typePredicate) { + this(Json.Default, typePredicate); + } + + /** + * Construct a new decoder using the provided {@link Json} instance which + * only decodes types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ public KotlinSerializationJsonDecoder(Json json) { - super(json, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"), - MediaType.APPLICATION_NDJSON); + super(json, DEFAULT_JSON_MIME_TYPES); + } + + /** + * Construct a new decoder using the provided {@link Json} instance which + * only decodes types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationJsonDecoder(Json json, Predicate typePredicate) { + super(json, typePredicate, DEFAULT_JSON_MIME_TYPES); } } 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 18983b162f4..4c3e22ec054 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 @@ -18,6 +18,7 @@ package org.springframework.http.codec.json; import java.util.List; import java.util.Map; +import java.util.function.Predicate; import kotlinx.serialization.json.Json; import org.jspecify.annotations.Nullable; @@ -38,23 +39,64 @@ import org.springframework.util.MimeType; * It supports {@code application/json}, {@code application/x-ndjson} and {@code application/*+json} with * various character sets, {@code UTF-8} being the default. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only encodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics + * level. + * + *

Alternative constructors with a {@code Predicate} + * parameter can be used to customize this behavior. For example, + * {@code new KotlinSerializationJsonEncoder(type -> true)} will encode all types + * supported by Kotlin Serialization, including unannotated Kotlin enumerations, + * numbers, characters, booleans and strings. * * @author Sebastien Deleuze * @author Iain Henderson * @since 5.3 + * @see KotlinSerializationJsonDecoder */ public class KotlinSerializationJsonEncoder extends KotlinSerializationStringEncoder { + private static final MimeType[] DEFAULT_JSON_MIME_TYPES = new MimeType[] { + MediaType.APPLICATION_JSON, + new MediaType("application", "*+json"), + MediaType.APPLICATION_NDJSON + }; + + /** + * Construct a new encoder using {@link Json.Default} instance which + * only encodes types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ public KotlinSerializationJsonEncoder() { this(Json.Default); } + /** + * Construct a new encoder using {@link Json.Default} instance which + * only encodes types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationJsonEncoder(Predicate typePredicate) { + this(Json.Default, typePredicate); + } + + /** + * Construct a new encoder using the provided {@link Json} instance which + * only encodes types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ public KotlinSerializationJsonEncoder(Json json) { - super(json, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"), - MediaType.APPLICATION_NDJSON); + super(json, DEFAULT_JSON_MIME_TYPES); + setStreamingMediaTypes(List.of(MediaType.APPLICATION_NDJSON)); + } + + /** + * Construct a new encoder using the provided {@link Json} instance which + * only encodes types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationJsonEncoder(Json json, Predicate typePredicate) { + super(json, typePredicate, DEFAULT_JSON_MIME_TYPES); setStreamingMediaTypes(List.of(MediaType.APPLICATION_NDJSON)); } diff --git a/spring-web/src/main/java/org/springframework/http/codec/protobuf/KotlinSerializationProtobufDecoder.java b/spring-web/src/main/java/org/springframework/http/codec/protobuf/KotlinSerializationProtobufDecoder.java index 44348cde620..4ea37699591 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/protobuf/KotlinSerializationProtobufDecoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/protobuf/KotlinSerializationProtobufDecoder.java @@ -16,8 +16,11 @@ package org.springframework.http.codec.protobuf; +import java.util.function.Predicate; + import kotlinx.serialization.protobuf.ProtoBuf; +import org.springframework.core.ResolvableType; import org.springframework.http.codec.KotlinSerializationBinaryDecoder; /** @@ -25,24 +28,61 @@ import org.springframework.http.codec.KotlinSerializationBinaryDecoder; * kotlinx.serialization. * It supports {@code application/x-protobuf}, {@code application/octet-stream}, and {@code application/vnd.google.protobuf}. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only decodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics + * level. + * + *

Alternative constructors with a {@code Predicate} + * parameter can be used to customize this behavior. For example, + * {@code new KotlinSerializationProtobufDecoder(type -> true)} will decode all types + * supported by Kotlin Serialization, including unannotated Kotlin enumerations, + * numbers, characters, booleans and strings. * *

Decoding streams is not supported yet, see * kotlinx.serialization/issues/1073 * related issue. * * @author Iain Henderson + * @author Sebastien Deleuze * @since 6.0 + * @see KotlinSerializationProtobufEncoder */ public class KotlinSerializationProtobufDecoder extends KotlinSerializationBinaryDecoder { + /** + * Construct a new decoder using {@link ProtoBuf.Default} instance which + * only decodes types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ + public KotlinSerializationProtobufDecoder() { this(ProtoBuf.Default); } + /** + * Construct a new decoder using {@link ProtoBuf.Default} instance which + * only decodes types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationProtobufDecoder(Predicate typePredicate) { + this(ProtoBuf.Default, typePredicate); + } + + /** + * Construct a new decoder using the provided {@link ProtoBuf} instance which + * only decodes types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ public KotlinSerializationProtobufDecoder(ProtoBuf protobuf) { super(protobuf, ProtobufCodecSupport.MIME_TYPES); } + + /** + * Construct a new decoder using the provided {@link ProtoBuf} instance which + * only decodes types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationProtobufDecoder(ProtoBuf protobuf, Predicate typePredicate) { + super(protobuf, typePredicate, ProtobufCodecSupport.MIME_TYPES); + } } diff --git a/spring-web/src/main/java/org/springframework/http/codec/protobuf/KotlinSerializationProtobufEncoder.java b/spring-web/src/main/java/org/springframework/http/codec/protobuf/KotlinSerializationProtobufEncoder.java index 2fcd82fd807..1530c29e709 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/protobuf/KotlinSerializationProtobufEncoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/protobuf/KotlinSerializationProtobufEncoder.java @@ -16,8 +16,11 @@ package org.springframework.http.codec.protobuf; +import java.util.function.Predicate; + import kotlinx.serialization.protobuf.ProtoBuf; +import org.springframework.core.ResolvableType; import org.springframework.http.codec.KotlinSerializationBinaryEncoder; /** @@ -25,24 +28,60 @@ import org.springframework.http.codec.KotlinSerializationBinaryEncoder; * kotlinx.serialization. * It supports {@code application/x-protobuf}, {@code application/octet-stream}, and {@code application/vnd.google.protobuf}. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only encodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics + * level. + * + *

Alternative constructors with a {@code Predicate} + * parameter can be used to customize this behavior. For example, + * {@code new KotlinSerializationProtobufEncoder(type -> true)} will encode all types + * supported by Kotlin Serialization, including unannotated Kotlin enumerations, + * numbers, characters, booleans and strings. * *

Decoding streams is not supported yet, see * kotlinx.serialization/issues/1073 * related issue. * * @author Iain Henderson + * @author Sebastien Deleuze * @since 6.0 + * @see KotlinSerializationProtobufDecoder */ public class KotlinSerializationProtobufEncoder extends KotlinSerializationBinaryEncoder { + /** + * Construct a new encoder using {@link ProtoBuf.Default} instance which + * only encodes types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ public KotlinSerializationProtobufEncoder() { this(ProtoBuf.Default); } + /** + * Construct a new encoder using {@link ProtoBuf.Default} instance which + * only encodes types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationProtobufEncoder(Predicate typePredicate) { + this(ProtoBuf.Default, typePredicate); + } + + /** + * Construct a new encoder using the provided {@link ProtoBuf} instance which + * only encodes types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ public KotlinSerializationProtobufEncoder(ProtoBuf protobuf) { super(protobuf, ProtobufCodecSupport.MIME_TYPES); } + + /** + * Construct a new encoder using the provided {@link ProtoBuf} instance which + * only encodes types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationProtobufEncoder(ProtoBuf protobuf, Predicate typePredicate) { + super(protobuf, typePredicate, ProtobufCodecSupport.MIME_TYPES); + } } diff --git a/spring-web/src/test/kotlin/org/springframework/http/codec/cbor/KotlinSerializationCborDecoderTests.kt b/spring-web/src/test/kotlin/org/springframework/http/codec/cbor/KotlinSerializationCborDecoderTests.kt index eca23093086..8d72de34dc8 100644 --- a/spring-web/src/test/kotlin/org/springframework/http/codec/cbor/KotlinSerializationCborDecoderTests.kt +++ b/spring-web/src/test/kotlin/org/springframework/http/codec/cbor/KotlinSerializationCborDecoderTests.kt @@ -49,22 +49,41 @@ class KotlinSerializationCborDecoderTests : AbstractDecoderTests { return Mono.defer { - val bytes = Cbor.Default.encodeToByteArray(serializer(Pojo::class.java), value) + val bytes = Cbor.encodeToByteArray(serializer(Pojo::class.java), value) val buffer = bufferFactory.allocateBuffer(bytes.size) buffer.write(bytes) Mono.just(buffer) diff --git a/spring-web/src/test/kotlin/org/springframework/http/codec/cbor/KotlinSerializationCborEncoderTests.kt b/spring-web/src/test/kotlin/org/springframework/http/codec/cbor/KotlinSerializationCborEncoderTests.kt index cc99d3003f7..3c9aa2f894d 100644 --- a/spring-web/src/test/kotlin/org/springframework/http/codec/cbor/KotlinSerializationCborEncoderTests.kt +++ b/spring-web/src/test/kotlin/org/springframework/http/codec/cbor/KotlinSerializationCborEncoderTests.kt @@ -49,10 +49,24 @@ class KotlinSerializationCborEncoderTests : AbstractEncoderTests -> step .consumeNextWith(expectBytes(pojoBytes) 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 483d06acce8..9e554d4a225 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 @@ -36,7 +36,7 @@ import java.nio.charset.StandardCharsets * @author Sebastien Deleuze */ class CustomKotlinSerializationJsonDecoderTests : - AbstractDecoderTests(KotlinSerializationJsonDecoder(customJson)) { + AbstractDecoderTests(KotlinSerializationJsonDecoder(customJson) { true }) { @Test override fun canDecode() { diff --git a/spring-web/src/test/kotlin/org/springframework/http/codec/json/CustomKotlinSerializationJsonEncoderTests.kt b/spring-web/src/test/kotlin/org/springframework/http/codec/json/CustomKotlinSerializationJsonEncoderTests.kt index 037dab3b1e9..65c5ae76cbc 100644 --- a/spring-web/src/test/kotlin/org/springframework/http/codec/json/CustomKotlinSerializationJsonEncoderTests.kt +++ b/spring-web/src/test/kotlin/org/springframework/http/codec/json/CustomKotlinSerializationJsonEncoderTests.kt @@ -34,7 +34,7 @@ import java.math.BigDecimal * @author Sebastien Deleuze */ class CustomKotlinSerializationJsonEncoderTests : - AbstractEncoderTests(KotlinSerializationJsonEncoder(customJson)) { + AbstractEncoderTests(KotlinSerializationJsonEncoder(customJson) { true }) { @Test override fun canEncode() { 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 6da9fcd2141..10689531892 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 @@ -59,13 +59,13 @@ class KotlinSerializationJsonDecoderTests : AbstractDecoderTests(KotlinSerializationJsonEncoder()) { @Test @@ -55,12 +54,51 @@ class KotlinSerializationJsonEncoderTests : AbstractEncoderTests(KotlinSerializationProtobufDecoder()) { +class KotlinSerializationProtobufDecoderTests : AbstractDecoderTests(KotlinSerializationProtobufDecoder { true }) { @Test override fun canDecode() { @@ -56,7 +56,7 @@ class KotlinSerializationProtobufDecoderTests : AbstractDecoderTests { return Mono.defer { - val bytes = ProtoBuf.Default.encodeToByteArray(serializer(Pojo::class.java), value) + val bytes = ProtoBuf.encodeToByteArray(serializer(Pojo::class.java), value) val buffer = bufferFactory.allocateBuffer(bytes.size) buffer.write(bytes) Mono.just(buffer) diff --git a/spring-web/src/test/kotlin/org/springframework/http/codec/protobuf/KotlinSerializationProtobufEncoderTests.kt b/spring-web/src/test/kotlin/org/springframework/http/codec/protobuf/KotlinSerializationProtobufEncoderTests.kt index 36a80859602..d61ab270b4c 100644 --- a/spring-web/src/test/kotlin/org/springframework/http/codec/protobuf/KotlinSerializationProtobufEncoderTests.kt +++ b/spring-web/src/test/kotlin/org/springframework/http/codec/protobuf/KotlinSerializationProtobufEncoderTests.kt @@ -29,6 +29,7 @@ import org.springframework.core.io.buffer.DataBufferUtils import org.springframework.core.testfixture.codec.AbstractEncoderTests import org.springframework.http.MediaType import org.springframework.http.codec.ServerSentEvent +import org.springframework.http.codec.cbor.KotlinSerializationCborEncoder import reactor.core.publisher.Flux import reactor.core.publisher.Mono import reactor.test.StepVerifier.FirstStep @@ -57,14 +58,33 @@ class KotlinSerializationProtobufEncoderTests : AbstractEncoderTests -> step .consumeNextWith(expectBytes(pojoBytes) @@ -90,7 +110,7 @@ class KotlinSerializationProtobufEncoderTests : AbstractEncoderTests -> step - .consumeNextWith(expectBytes(ProtoBuf.Default.encodeToByteArray(pojo)) + .consumeNextWith(expectBytes(ProtoBuf.encodeToByteArray(pojo)) .andThen { dataBuffer: DataBuffer -> DataBufferUtils.release(dataBuffer) }) .verifyComplete() }