From 5f4d233cc86b1d7df9ed835f8db82e002181c476 Mon Sep 17 00:00:00 2001 From: noah3521 Date: Sat, 23 May 2026 05:59:45 +0900 Subject: [PATCH] Restore Jackson XML participation in WebFlux codecs Register Jackson XML after JAXB so WebFlux keeps JAXB-first behavior while allowing Jackson XML when JAXB is also present. Keep raw String values on the String codec path by applying XML-level Jackson String guards. Closes gh-36776 Closes gh-36775 Constraint: Avoid MVC parity claims; keep WebFlux JAXB first. Rejected: Jackson-first XML registration | changes JAXB behavior. Confidence: high Scope-risk: moderate Directive: Keep XML codec ordering tests explicit. Tested: Targeted spring-web codec/configurer test suite. Tested: ./gradlew :spring-web:check --no-configuration-cache Not-tested: Full repository check. Signed-off-by: noah3521 --- .../http/codec/support/BaseDefaultCodecs.java | 4 +-- .../http/codec/xml/JacksonXmlDecoder.java | 5 +++ .../http/codec/xml/JacksonXmlEncoder.java | 5 +++ .../support/ClientCodecConfigurerTests.java | 24 ++++++++++---- .../codec/support/CodecConfigurerTests.java | 32 ++++++++++++++----- .../support/ServerCodecConfigurerTests.java | 15 +++++++-- .../codec/xml/JacksonXmlDecoderTests.java | 6 +++- .../codec/xml/JacksonXmlEncoderTests.java | 4 ++- 8 files changed, 75 insertions(+), 20 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java b/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java index 2aff56ebf7e7..1abbd871e723 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java +++ b/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java @@ -679,7 +679,7 @@ else if (GSON_PRESENT) { addCodec(this.objectReaders, new DecoderHttpMessageReader<>(this.jaxb2Decoder != null ? (Jaxb2XmlDecoder) this.jaxb2Decoder : new Jaxb2XmlDecoder())); } - else if(JACKSON_XML_PRESENT) { + if (JACKSON_XML_PRESENT) { addCodec(this.objectReaders, new DecoderHttpMessageReader<>(getJacksonXmlDecoder())); } if (KOTLIN_SERIALIZATION_PROTOBUF_PRESENT) { @@ -821,7 +821,7 @@ else if (GSON_PRESENT) { addCodec(writers, new EncoderHttpMessageWriter<>(this.jaxb2Encoder != null ? (Jaxb2XmlEncoder) this.jaxb2Encoder : new Jaxb2XmlEncoder())); } - else if (JACKSON_XML_PRESENT) { + if (JACKSON_XML_PRESENT) { addCodec(writers, new EncoderHttpMessageWriter<>(getJacksonXmlEncoder())); } if (KOTLIN_SERIALIZATION_PROTOBUF_PRESENT) { diff --git a/spring-web/src/main/java/org/springframework/http/codec/xml/JacksonXmlDecoder.java b/spring-web/src/main/java/org/springframework/http/codec/xml/JacksonXmlDecoder.java index d75e13f692bf..c9c4a9725df2 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/xml/JacksonXmlDecoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/xml/JacksonXmlDecoder.java @@ -102,6 +102,11 @@ public JacksonXmlDecoder(XmlMapper mapper, MimeType... mimeTypes) { } + @Override + public boolean canDecode(ResolvableType elementType, @Nullable MimeType mimeType) { + return super.canDecode(elementType, mimeType) && !CharSequence.class.isAssignableFrom(elementType.toClass()); + } + @Override public Flux decode(Publisher input, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints) { diff --git a/spring-web/src/main/java/org/springframework/http/codec/xml/JacksonXmlEncoder.java b/spring-web/src/main/java/org/springframework/http/codec/xml/JacksonXmlEncoder.java index a298a7b3d0fb..412a6f0b1787 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/xml/JacksonXmlEncoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/xml/JacksonXmlEncoder.java @@ -97,6 +97,11 @@ public JacksonXmlEncoder(XmlMapper mapper, MimeType... mimeTypes) { } + @Override + public boolean canEncode(ResolvableType elementType, @Nullable MimeType mimeType) { + return super.canEncode(elementType, mimeType) && !String.class.isAssignableFrom(elementType.toClass()); + } + @Override public Flux encode(Publisher inputStream, DataBufferFactory bufferFactory, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints) { diff --git a/spring-web/src/test/java/org/springframework/http/codec/support/ClientCodecConfigurerTests.java b/spring-web/src/test/java/org/springframework/http/codec/support/ClientCodecConfigurerTests.java index e12d24383232..08328eed68af 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/support/ClientCodecConfigurerTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/support/ClientCodecConfigurerTests.java @@ -72,6 +72,8 @@ import org.springframework.http.codec.protobuf.ProtobufHttpMessageWriter; import org.springframework.http.codec.smile.JacksonSmileDecoder; import org.springframework.http.codec.smile.JacksonSmileEncoder; +import org.springframework.http.codec.xml.JacksonXmlDecoder; +import org.springframework.http.codec.xml.JacksonXmlEncoder; import org.springframework.http.codec.xml.Jaxb2XmlDecoder; import org.springframework.http.codec.xml.Jaxb2XmlEncoder; import org.springframework.util.MimeTypeUtils; @@ -95,7 +97,7 @@ class ClientCodecConfigurerTests { @Test void defaultReaders() { List> readers = this.configurer.getReaders(); - assertThat(readers).hasSize(20); + assertThat(readers).hasSize(21); assertThat(getNextDecoder(readers).getClass()).isEqualTo(ByteArrayDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(ByteBufferDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(DataBufferDecoder.class); @@ -114,6 +116,7 @@ void defaultReaders() { assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationCborDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonCborDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jaxb2XmlDecoder.class); + assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonXmlDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationProtobufDecoder.class); assertSseReader(readers); assertStringDecoder(getNextDecoder(readers), false); @@ -122,7 +125,7 @@ void defaultReaders() { @Test void defaultWriters() { List> writers = this.configurer.getWriters(); - assertThat(writers).hasSize(18); + assertThat(writers).hasSize(19); assertThat(getNextEncoder(writers).getClass()).isEqualTo(ByteArrayEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(ByteBufferEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(DataBufferEncoder.class); @@ -139,6 +142,7 @@ void defaultWriters() { assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationCborEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonCborEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jaxb2XmlEncoder.class); + assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonXmlEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationProtobufEncoder.class); assertStringEncoder(getNextEncoder(writers), false); } @@ -147,21 +151,28 @@ void defaultWriters() { void jacksonCodecCustomization() { JacksonJsonDecoder decoder = new JacksonJsonDecoder(); JacksonJsonEncoder encoder = new JacksonJsonEncoder(); + JacksonXmlDecoder xmlDecoder = new JacksonXmlDecoder(); + JacksonXmlEncoder xmlEncoder = new JacksonXmlEncoder(); this.configurer.defaultCodecs().jacksonJsonDecoder(decoder); this.configurer.defaultCodecs().jacksonJsonEncoder(encoder); + this.configurer.defaultCodecs().jacksonXmlDecoder(xmlDecoder); + this.configurer.defaultCodecs().jacksonXmlEncoder(xmlEncoder); List> readers = this.configurer.getReaders(); JacksonJsonDecoder actualDecoder = findCodec(readers, JacksonJsonDecoder.class); assertThat(actualDecoder).isSameAs(decoder); + assertThat(findCodec(readers, JacksonXmlDecoder.class)).isSameAs(xmlDecoder); assertThat(findCodec(readers, ServerSentEventHttpMessageReader.class).getDecoder()).isSameAs(decoder); List> writers = this.configurer.getWriters(); JacksonJsonEncoder actualEncoder = findCodec(writers, JacksonJsonEncoder.class); assertThat(actualEncoder).isSameAs(encoder); + assertThat(findCodec(writers, JacksonXmlEncoder.class)).isSameAs(xmlEncoder); MultipartHttpMessageWriter multipartWriter = findCodec(writers, MultipartHttpMessageWriter.class); actualEncoder = findCodec(multipartWriter.getPartWriters(), JacksonJsonEncoder.class); assertThat(actualEncoder).isSameAs(encoder); + assertThat(findCodec(multipartWriter.getPartWriters(), JacksonXmlEncoder.class)).isSameAs(xmlEncoder); } @Test @@ -169,7 +180,7 @@ void maxInMemorySize() { int size = 99; this.configurer.defaultCodecs().maxInMemorySize(size); List> readers = this.configurer.getReaders(); - assertThat(readers).hasSize(20); + assertThat(readers).hasSize(21); assertThat(((ByteArrayDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); assertThat(((ByteBufferDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); assertThat(((DataBufferDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); @@ -187,6 +198,7 @@ void maxInMemorySize() { assertThat(((KotlinSerializationCborDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); assertThat(((JacksonCborDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); assertThat(((Jaxb2XmlDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); + assertThat(((JacksonXmlDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); assertThat(((KotlinSerializationProtobufDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); ServerSentEventHttpMessageReader reader = (ServerSentEventHttpMessageReader) nextReader(readers); @@ -232,7 +244,7 @@ void clonedConfigurer() { writers = findCodec(this.configurer.getWriters(), MultipartHttpMessageWriter.class).getPartWriters(); assertThat(sseDecoder).isNotSameAs(jacksonDecoder); - assertThat(writers).hasSize(18); + assertThat(writers).hasSize(19); } @Test // gh-24194 @@ -242,7 +254,7 @@ void cloneShouldNotDropMultipartCodecs() { List> writers = findCodec(clone.getWriters(), MultipartHttpMessageWriter.class).getPartWriters(); - assertThat(writers).hasSize(18); + assertThat(writers).hasSize(19); } @Test @@ -256,7 +268,7 @@ void cloneShouldNotBeImpactedByChangesToOriginal() { List> writers = findCodec(clone.getWriters(), MultipartHttpMessageWriter.class).getPartWriters(); - assertThat(writers).hasSize(18); + assertThat(writers).hasSize(19); } private Decoder getNextDecoder(List> readers) { diff --git a/spring-web/src/test/java/org/springframework/http/codec/support/CodecConfigurerTests.java b/spring-web/src/test/java/org/springframework/http/codec/support/CodecConfigurerTests.java index f5a2681496aa..ae5014d3d6e9 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/support/CodecConfigurerTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/support/CodecConfigurerTests.java @@ -68,6 +68,8 @@ import org.springframework.http.codec.protobuf.ProtobufHttpMessageWriter; import org.springframework.http.codec.smile.JacksonSmileDecoder; import org.springframework.http.codec.smile.JacksonSmileEncoder; +import org.springframework.http.codec.xml.JacksonXmlDecoder; +import org.springframework.http.codec.xml.JacksonXmlEncoder; import org.springframework.http.codec.xml.Jaxb2XmlDecoder; import org.springframework.http.codec.xml.Jaxb2XmlEncoder; import org.springframework.util.MimeTypeUtils; @@ -92,7 +94,7 @@ class CodecConfigurerTests { @Test void defaultReaders() { List> readers = this.configurer.getReaders(); - assertThat(readers).hasSize(19); + assertThat(readers).hasSize(20); assertThat(getNextDecoder(readers).getClass()).isEqualTo(ByteArrayDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(ByteBufferDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(DataBufferDecoder.class); @@ -110,6 +112,7 @@ void defaultReaders() { assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationCborDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonCborDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jaxb2XmlDecoder.class); + assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonXmlDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationProtobufDecoder.class); assertStringDecoder(getNextDecoder(readers), false); } @@ -117,7 +120,7 @@ void defaultReaders() { @Test void defaultWriters() { List> writers = this.configurer.getWriters(); - assertThat(writers).hasSize(18); + assertThat(writers).hasSize(19); assertThat(getNextEncoder(writers).getClass()).isEqualTo(ByteArrayEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(ByteBufferEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(DataBufferEncoder.class); @@ -134,6 +137,7 @@ void defaultWriters() { assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationCborEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonCborEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jaxb2XmlEncoder.class); + assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonXmlEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationProtobufEncoder.class); assertStringEncoder(getNextEncoder(writers), false); } @@ -160,7 +164,7 @@ void defaultAndCustomReaders() { List> readers = this.configurer.getReaders(); - assertThat(readers).hasSize(23); + assertThat(readers).hasSize(24); assertThat(getNextDecoder(readers)).isSameAs(customDecoder1); assertThat(readers.get(this.index.getAndIncrement())).isSameAs(customReader1); assertThat(getNextDecoder(readers).getClass()).isEqualTo(ByteArrayDecoder.class); @@ -182,6 +186,7 @@ void defaultAndCustomReaders() { assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationCborDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonCborDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jaxb2XmlDecoder.class); + assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonXmlDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationProtobufDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(StringDecoder.class); } @@ -208,7 +213,7 @@ void defaultAndCustomWriters() { List> writers = this.configurer.getWriters(); - assertThat(writers).hasSize(22); + assertThat(writers).hasSize(23); assertThat(getNextEncoder(writers)).isSameAs(customEncoder1); assertThat(writers.get(this.index.getAndIncrement())).isSameAs(customWriter1); assertThat(getNextEncoder(writers).getClass()).isEqualTo(ByteArrayEncoder.class); @@ -229,6 +234,7 @@ void defaultAndCustomWriters() { assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationCborEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonCborEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jaxb2XmlEncoder.class); + assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonXmlEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationProtobufEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(CharSequenceEncoder.class); } @@ -299,6 +305,8 @@ void defaultsOffWithCustomWriters() { void encoderDecoderOverrides() { JacksonJsonDecoder jacksonDecoder = new JacksonJsonDecoder(); JacksonJsonEncoder jacksonEncoder = new JacksonJsonEncoder(); + JacksonXmlDecoder jacksonXmlDecoder = new JacksonXmlDecoder(); + JacksonXmlEncoder jacksonXmlEncoder = new JacksonXmlEncoder(); JacksonSmileDecoder smileDecoder = new JacksonSmileDecoder(); JacksonSmileEncoder smileEncoder = new JacksonSmileEncoder(); ProtobufDecoder protobufDecoder = new ProtobufDecoder(ExtensionRegistry.newInstance()); @@ -308,6 +316,8 @@ void encoderDecoderOverrides() { this.configurer.defaultCodecs().jacksonJsonDecoder(jacksonDecoder); this.configurer.defaultCodecs().jacksonJsonEncoder(jacksonEncoder); + this.configurer.defaultCodecs().jacksonXmlDecoder(jacksonXmlDecoder); + this.configurer.defaultCodecs().jacksonXmlEncoder(jacksonXmlEncoder); this.configurer.defaultCodecs().jacksonSmileDecoder(smileDecoder); this.configurer.defaultCodecs().jacksonSmileEncoder(smileEncoder); this.configurer.defaultCodecs().protobufDecoder(protobufDecoder); @@ -316,10 +326,12 @@ void encoderDecoderOverrides() { this.configurer.defaultCodecs().jaxb2Encoder(jaxb2Encoder); assertDecoderInstance(jacksonDecoder); + assertDecoderInstance(jacksonXmlDecoder); assertDecoderInstance(smileDecoder); assertDecoderInstance(protobufDecoder); assertDecoderInstance(jaxb2Decoder); assertEncoderInstance(jacksonEncoder); + assertEncoderInstance(jacksonXmlEncoder); assertEncoderInstance(smileEncoder); assertEncoderInstance(protobufEncoder); assertEncoderInstance(jaxb2Encoder); @@ -369,6 +381,8 @@ void cloneDefaultCodecs() { JacksonJsonDecoder jacksonDecoder = new JacksonJsonDecoder(); JacksonJsonEncoder jacksonEncoder = new JacksonJsonEncoder(); + JacksonXmlDecoder jacksonXmlDecoder = new JacksonXmlDecoder(); + JacksonXmlEncoder jacksonXmlEncoder = new JacksonXmlEncoder(); Jaxb2XmlDecoder jaxb2Decoder = new Jaxb2XmlDecoder(); Jaxb2XmlEncoder jaxb2Encoder = new Jaxb2XmlEncoder(); ProtobufDecoder protoDecoder = new ProtobufDecoder(); @@ -376,6 +390,8 @@ void cloneDefaultCodecs() { clone.defaultCodecs().jacksonJsonDecoder(jacksonDecoder); clone.defaultCodecs().jacksonJsonEncoder(jacksonEncoder); + clone.defaultCodecs().jacksonXmlDecoder(jacksonXmlDecoder); + clone.defaultCodecs().jacksonXmlEncoder(jacksonXmlEncoder); clone.defaultCodecs().jaxb2Decoder(jaxb2Decoder); clone.defaultCodecs().jaxb2Encoder(jaxb2Encoder); clone.defaultCodecs().protobufDecoder(protoDecoder); @@ -393,8 +409,8 @@ void cloneDefaultCodecs() { .map(reader -> ((EncoderHttpMessageWriter) reader).getEncoder()) .collect(Collectors.toList()); - assertThat(decoders).contains(jacksonDecoder, jaxb2Decoder, protoDecoder); - assertThat(encoders).contains(jacksonEncoder, jaxb2Encoder, protoEncoder); + assertThat(decoders).contains(jacksonDecoder, jacksonXmlDecoder, jaxb2Decoder, protoDecoder); + assertThat(encoders).contains(jacksonEncoder, jacksonXmlEncoder, jaxb2Encoder, protoEncoder); // Original does not have the customizations @@ -408,8 +424,8 @@ void cloneDefaultCodecs() { .map(reader -> ((EncoderHttpMessageWriter) reader).getEncoder()) .collect(Collectors.toList()); - assertThat(decoders).doesNotContain(jacksonDecoder, jaxb2Decoder, protoDecoder); - assertThat(encoders).doesNotContain(jacksonEncoder, jaxb2Encoder, protoEncoder); + assertThat(decoders).doesNotContain(jacksonDecoder, jacksonXmlDecoder, jaxb2Decoder, protoDecoder); + assertThat(encoders).doesNotContain(jacksonEncoder, jacksonXmlEncoder, jaxb2Encoder, protoEncoder); } private Decoder getNextDecoder(List> readers) { diff --git a/spring-web/src/test/java/org/springframework/http/codec/support/ServerCodecConfigurerTests.java b/spring-web/src/test/java/org/springframework/http/codec/support/ServerCodecConfigurerTests.java index c6565730b872..5858c1b98a7a 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/support/ServerCodecConfigurerTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/support/ServerCodecConfigurerTests.java @@ -72,6 +72,8 @@ import org.springframework.http.codec.protobuf.ProtobufHttpMessageWriter; import org.springframework.http.codec.smile.JacksonSmileDecoder; import org.springframework.http.codec.smile.JacksonSmileEncoder; +import org.springframework.http.codec.xml.JacksonXmlDecoder; +import org.springframework.http.codec.xml.JacksonXmlEncoder; import org.springframework.http.codec.xml.Jaxb2XmlDecoder; import org.springframework.http.codec.xml.Jaxb2XmlEncoder; import org.springframework.util.MimeTypeUtils; @@ -95,7 +97,7 @@ class ServerCodecConfigurerTests { @Test void defaultReaders() { List> readers = this.configurer.getReaders(); - assertThat(readers).hasSize(19); + assertThat(readers).hasSize(20); assertThat(getNextDecoder(readers).getClass()).isEqualTo(ByteArrayDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(ByteBufferDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(DataBufferDecoder.class); @@ -113,6 +115,7 @@ void defaultReaders() { assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationCborDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonCborDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jaxb2XmlDecoder.class); + assertThat(getNextDecoder(readers).getClass()).isEqualTo(JacksonXmlDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationProtobufDecoder.class); assertStringDecoder(getNextDecoder(readers), false); } @@ -120,7 +123,7 @@ void defaultReaders() { @Test void defaultWriters() { List> writers = this.configurer.getWriters(); - assertThat(writers).hasSize(19); + assertThat(writers).hasSize(20); assertThat(getNextEncoder(writers).getClass()).isEqualTo(ByteArrayEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(ByteBufferEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(DataBufferEncoder.class); @@ -137,6 +140,7 @@ void defaultWriters() { assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationCborEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonCborEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(Jaxb2XmlEncoder.class); + assertThat(getNextEncoder(writers).getClass()).isEqualTo(JacksonXmlEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationProtobufEncoder.class); assertSseWriter(writers); assertStringEncoder(getNextEncoder(writers), false); @@ -146,16 +150,22 @@ void defaultWriters() { void jacksonEncoderOverride() { JacksonJsonDecoder decoder = new JacksonJsonDecoder(); JacksonJsonEncoder encoder = new JacksonJsonEncoder(); + JacksonXmlDecoder xmlDecoder = new JacksonXmlDecoder(); + JacksonXmlEncoder xmlEncoder = new JacksonXmlEncoder(); this.configurer.defaultCodecs().jacksonJsonDecoder(decoder); this.configurer.defaultCodecs().jacksonJsonEncoder(encoder); + this.configurer.defaultCodecs().jacksonXmlDecoder(xmlDecoder); + this.configurer.defaultCodecs().jacksonXmlEncoder(xmlEncoder); List> readers = this.configurer.getReaders(); JacksonJsonDecoder actualDecoder = findCodec(readers, JacksonJsonDecoder.class); assertThat(actualDecoder).isSameAs(decoder); + assertThat(findCodec(readers, JacksonXmlDecoder.class)).isSameAs(xmlDecoder); List> writers = this.configurer.getWriters(); JacksonJsonEncoder actualEncoder = findCodec(writers, JacksonJsonEncoder.class); assertThat(actualEncoder).isSameAs(encoder); + assertThat(findCodec(writers, JacksonXmlEncoder.class)).isSameAs(xmlEncoder); assertThat(findCodec(writers, ServerSentEventHttpMessageWriter.class).getEncoder()).isSameAs(encoder); } @@ -186,6 +196,7 @@ void maxInMemorySize() { assertThat(((KotlinSerializationCborDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); assertThat(((JacksonCborDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); assertThat(((Jaxb2XmlDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); + assertThat(((JacksonXmlDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); assertThat(((KotlinSerializationProtobufDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); assertThat(((StringDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); } diff --git a/spring-web/src/test/java/org/springframework/http/codec/xml/JacksonXmlDecoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/xml/JacksonXmlDecoderTests.java index b9c622147992..b6ece94fcd35 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/xml/JacksonXmlDecoderTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/xml/JacksonXmlDecoderTests.java @@ -55,11 +55,15 @@ public JacksonXmlDecoderTests() { @Override @Test protected void canDecode() { + ResolvableType stringType = ResolvableType.forClass(String.class); + ResolvableType charSequenceType = ResolvableType.forClass(CharSequence.class); assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), MediaType.APPLICATION_XML)).isTrue(); assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), MediaType.TEXT_XML)).isTrue(); assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), new MediaType("application", "soap+xml"))).isTrue(); assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), null)).isTrue(); - assertThat(decoder.canDecode(ResolvableType.forClass(String.class), null)).isTrue(); + assertThat(decoder.canDecode(stringType, MediaType.APPLICATION_XML)).isFalse(); + assertThat(decoder.canDecode(stringType, null)).isFalse(); + assertThat(decoder.canDecode(charSequenceType, MediaType.APPLICATION_XML)).isFalse(); assertThat(decoder.canDecode(ResolvableType.forClass(Pojo.class), MediaType.APPLICATION_JSON)).isFalse(); } diff --git a/spring-web/src/test/java/org/springframework/http/codec/xml/JacksonXmlEncoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/xml/JacksonXmlEncoderTests.java index d7a2d7ba8cae..963bdf118ce0 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/xml/JacksonXmlEncoderTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/xml/JacksonXmlEncoderTests.java @@ -48,11 +48,13 @@ class JacksonXmlEncoderTests extends AbstractLeakCheckingTests { @Test void canEncode() { ResolvableType pojoType = ResolvableType.forClass(Pojo.class); + ResolvableType stringType = ResolvableType.forClass(String.class); assertThat(this.encoder.canEncode(pojoType, MediaType.APPLICATION_XML)).isTrue(); assertThat(this.encoder.canEncode(pojoType, MediaType.TEXT_XML)).isTrue(); assertThat(this.encoder.canEncode(pojoType, new MediaType("application", "soap+xml"))).isTrue(); assertThat(this.encoder.canEncode(pojoType, null)).isTrue(); - assertThat(this.encoder.canEncode(ResolvableType.forClass(String.class), null)).isTrue(); + assertThat(this.encoder.canEncode(stringType, MediaType.APPLICATION_XML)).isFalse(); + assertThat(this.encoder.canEncode(stringType, null)).isFalse(); assertThat(this.encoder.canEncode(ResolvableType.NONE, null)).isTrue(); assertThat(this.encoder.canEncode(ResolvableType.forClass(Pojo.class), MediaType.APPLICATION_JSON)).isFalse(); }