Configurable Marshaller/Unmarshaller in JAXB2 codecs
Issue: SPR-17388
This commit is contained in:
@@ -127,6 +127,22 @@ public interface CodecConfigurer {
|
||||
*/
|
||||
void protobufEncoder(Encoder<?> encoder);
|
||||
|
||||
/**
|
||||
* Override the default JAXB2 {@code Decoder}.
|
||||
* @param decoder the decoder instance to use
|
||||
* @since 5.1.3
|
||||
* @see org.springframework.http.codec.xml.Jaxb2XmlDecoder
|
||||
*/
|
||||
void jaxb2Decoder(Decoder<?> decoder);
|
||||
|
||||
/**
|
||||
* Override the default JABX2 {@code Encoder}.
|
||||
* @param encoder the encoder instance to use
|
||||
* @since 5.1.3
|
||||
* @see org.springframework.http.codec.xml.Jaxb2XmlEncoder
|
||||
*/
|
||||
void jaxb2Encoder(Encoder<?> encoder);
|
||||
|
||||
/**
|
||||
* Whether to log form data at DEBUG level, and headers at TRACE level.
|
||||
* Both may contain sensitive information.
|
||||
|
||||
@@ -89,6 +89,12 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
|
||||
@Nullable
|
||||
private Encoder<?> protobufEncoder;
|
||||
|
||||
@Nullable
|
||||
private Decoder<?> jaxb2Decoder;
|
||||
|
||||
@Nullable
|
||||
private Encoder<?> jaxb2Encoder;
|
||||
|
||||
private boolean enableLoggingRequestDetails = false;
|
||||
|
||||
private boolean registerDefaults = true;
|
||||
@@ -114,6 +120,16 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
|
||||
this.protobufEncoder = encoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jaxb2Decoder(Decoder<?> decoder) {
|
||||
this.jaxb2Decoder = decoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jaxb2Encoder(Encoder<?> encoder) {
|
||||
this.jaxb2Encoder = encoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableLoggingRequestDetails(boolean enable) {
|
||||
this.enableLoggingRequestDetails = enable;
|
||||
@@ -145,7 +161,8 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
|
||||
readers.add(new DecoderHttpMessageReader<>(new ResourceDecoder()));
|
||||
readers.add(new DecoderHttpMessageReader<>(StringDecoder.textPlainOnly()));
|
||||
if (protobufPresent) {
|
||||
readers.add(new DecoderHttpMessageReader<>(getProtobufDecoder()));
|
||||
Decoder<?> decoder = this.protobufDecoder != null ? this.protobufDecoder : new ProtobufDecoder();
|
||||
readers.add(new DecoderHttpMessageReader<>(decoder));
|
||||
}
|
||||
|
||||
FormHttpMessageReader formReader = new FormHttpMessageReader();
|
||||
@@ -178,7 +195,8 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
|
||||
readers.add(new DecoderHttpMessageReader<>(new Jackson2SmileDecoder()));
|
||||
}
|
||||
if (jaxb2Present) {
|
||||
readers.add(new DecoderHttpMessageReader<>(new Jaxb2XmlDecoder()));
|
||||
Decoder<?> decoder = this.jaxb2Decoder != null ? this.jaxb2Decoder : new Jaxb2XmlDecoder();
|
||||
readers.add(new DecoderHttpMessageReader<>(decoder));
|
||||
}
|
||||
extendObjectReaders(readers);
|
||||
return readers;
|
||||
@@ -224,7 +242,8 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
|
||||
extendTypedWriters(writers);
|
||||
}
|
||||
if (protobufPresent) {
|
||||
writers.add(new ProtobufHttpMessageWriter((Encoder) getProtobufEncoder()));
|
||||
Encoder<?> encoder = this.protobufEncoder != null ? this.protobufEncoder : new ProtobufEncoder();
|
||||
writers.add(new ProtobufHttpMessageWriter((Encoder) encoder));
|
||||
}
|
||||
return writers;
|
||||
}
|
||||
@@ -253,7 +272,8 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
|
||||
writers.add(new EncoderHttpMessageWriter<>(new Jackson2SmileEncoder()));
|
||||
}
|
||||
if (jaxb2Present) {
|
||||
writers.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
|
||||
Encoder<?> encoder = this.jaxb2Encoder != null ? this.jaxb2Encoder : new Jaxb2XmlEncoder();
|
||||
writers.add(new EncoderHttpMessageWriter<>(encoder));
|
||||
}
|
||||
// No client or server specific multipart writers currently..
|
||||
if (!forMultipart) {
|
||||
@@ -291,12 +311,4 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
|
||||
return (this.jackson2JsonEncoder != null ? this.jackson2JsonEncoder : new Jackson2JsonEncoder());
|
||||
}
|
||||
|
||||
protected Decoder<?> getProtobufDecoder() {
|
||||
return (this.protobufDecoder != null ? this.protobufDecoder : new ProtobufDecoder());
|
||||
}
|
||||
|
||||
protected Encoder<?> getProtobufEncoder() {
|
||||
return (this.protobufEncoder != null ? this.protobufEncoder : new ProtobufEncoder());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -75,12 +75,32 @@ public class Jaxb2XmlDecoder extends AbstractDecoder<Object> {
|
||||
|
||||
private final JaxbContextContainer jaxbContexts = new JaxbContextContainer();
|
||||
|
||||
private Function<Unmarshaller, Unmarshaller> unmarshallerProcessor = Function.identity();
|
||||
|
||||
|
||||
public Jaxb2XmlDecoder() {
|
||||
super(MimeTypeUtils.APPLICATION_XML, MimeTypeUtils.TEXT_XML);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configure a processor function to customize Unmarshaller instances.
|
||||
* @param processor the function to use
|
||||
* @since 5.1.3
|
||||
*/
|
||||
public void setUnmarshallerProcessor(Function<Unmarshaller, Unmarshaller> processor) {
|
||||
this.unmarshallerProcessor = this.unmarshallerProcessor.andThen(processor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured processor for customizing Unmarshaller instances.
|
||||
* @since 5.1.3
|
||||
*/
|
||||
public Function<Unmarshaller, Unmarshaller> getUnmarshallerProcessor() {
|
||||
return this.unmarshallerProcessor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canDecode(ResolvableType elementType, @Nullable MimeType mimeType) {
|
||||
if (super.canDecode(elementType, mimeType)) {
|
||||
@@ -123,7 +143,7 @@ public class Jaxb2XmlDecoder extends AbstractDecoder<Object> {
|
||||
|
||||
private Object unmarshal(List<XMLEvent> events, Class<?> outputClass) {
|
||||
try {
|
||||
Unmarshaller unmarshaller = this.jaxbContexts.createUnmarshaller(outputClass);
|
||||
Unmarshaller unmarshaller = initUnmarshaller(outputClass);
|
||||
XMLEventReader eventReader = StaxUtils.createXMLEventReader(events);
|
||||
if (outputClass.isAnnotationPresent(XmlRootElement.class)) {
|
||||
return unmarshaller.unmarshal(eventReader);
|
||||
@@ -141,6 +161,11 @@ public class Jaxb2XmlDecoder extends AbstractDecoder<Object> {
|
||||
}
|
||||
}
|
||||
|
||||
private Unmarshaller initUnmarshaller(Class<?> outputClass) throws JAXBException {
|
||||
Unmarshaller unmarshaller = this.jaxbContexts.createUnmarshaller(outputClass);
|
||||
return this.unmarshallerProcessor.apply(unmarshaller);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the qualified name for the given class, according to the mapping rules
|
||||
* in the JAXB specification.
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.http.codec.xml;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.MarshalException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
@@ -57,12 +58,32 @@ public class Jaxb2XmlEncoder extends AbstractSingleValueEncoder<Object> {
|
||||
|
||||
private final JaxbContextContainer jaxbContexts = new JaxbContextContainer();
|
||||
|
||||
private Function<Marshaller, Marshaller> marshallerProcessor = Function.identity();
|
||||
|
||||
|
||||
public Jaxb2XmlEncoder() {
|
||||
super(MimeTypeUtils.APPLICATION_XML, MimeTypeUtils.TEXT_XML);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configure a processor function to customize Marshaller instances.
|
||||
* @param processor the function to use
|
||||
* @since 5.1.3
|
||||
*/
|
||||
public void setMarshallerProcessor(Function<Marshaller, Marshaller> processor) {
|
||||
this.marshallerProcessor = this.marshallerProcessor.andThen(processor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured processor for customizing Marshaller instances.
|
||||
* @since 5.1.3
|
||||
*/
|
||||
public Function<Marshaller, Marshaller> getMarshallerProcessor() {
|
||||
return this.marshallerProcessor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canEncode(ResolvableType elementType, @Nullable MimeType mimeType) {
|
||||
if (super.canEncode(elementType, mimeType)) {
|
||||
@@ -92,8 +113,7 @@ public class Jaxb2XmlEncoder extends AbstractSingleValueEncoder<Object> {
|
||||
Class<?> clazz = ClassUtils.getUserClass(value);
|
||||
|
||||
try {
|
||||
Marshaller marshaller = this.jaxbContexts.createMarshaller(clazz);
|
||||
marshaller.setProperty(Marshaller.JAXB_ENCODING, StandardCharsets.UTF_8.name());
|
||||
Marshaller marshaller = initMarshaller(clazz);
|
||||
marshaller.marshal(value, outputStream);
|
||||
release = false;
|
||||
return Flux.just(buffer);
|
||||
@@ -112,4 +132,11 @@ public class Jaxb2XmlEncoder extends AbstractSingleValueEncoder<Object> {
|
||||
}
|
||||
}
|
||||
|
||||
private Marshaller initMarshaller(Class<?> clazz) throws JAXBException {
|
||||
Marshaller marshaller = this.jaxbContexts.createMarshaller(clazz);
|
||||
marshaller.setProperty(Marshaller.JAXB_ENCODING, StandardCharsets.UTF_8.name());
|
||||
marshaller = this.marshallerProcessor.apply(marshaller);
|
||||
return marshaller;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user