Skip to content

Charset for input stream ignored in Jaxb2XmlDecoder #28599

Closed
@mo-zachery-harley

Description

@mo-zachery-harley

Spring Web 5.3.18
Affected class: Jaxb2XmlDecoder

I am making a web request to a service that returnes XML, and having it decoded into a custom object.
The response is encoded using "ISO-8859-1" and the charset is included in the response header, however, it's not used
when building the XMLEventReader in Jaxb2XmlDecoder.

I have run the service against a mocked service producing the exact same response just encoded with UTF-8, and the object was unmarshalled correctly. The issues is the Jaxb2xmlDecoder attempts to unmashal in the method decode using the following:

try {
	Iterator eventReader = inputFactory.createXMLEventReader(dataBuffer.asInputStream());
	List<XMLEvent> events = new ArrayList<>();
	eventReader.forEachRemaining(event -> events.add((XMLEvent) event));
	return unmarshal(events, targetType.toClass());
}

The change required to use the correct encoding would be to replace

inputFactory.createXMLEventReader(dataBuffer.asInputStream());

with

inputFactory.createXMLEventReader(dataBuffer.asInputStream(), mimeType.getCharset());

For completness, below is my current implementation.
The WebClient used to make the request is created using:

WebClient.builder()
.codecs(clientCodecs -> {
    clientCodecs.defaultCodecs().jaxb2Encoder(new Jaxb2XmlEncoder());
    clientCodecs.defaultCodecs().jaxb2Decoder(new Jaxb2XmlDecoder());
}).build();

And the request itself is:

this.webClient.get()
    .uri(uri)
    .headers(headers -> headers.addAll(getHeaders))
    .retrieve()
    .bodyToMono(MyClass.class)
    .block();

The headers set are for the content type being application/xml

The exception thrown is:

Caused by: java.util.NoSuchElementException: ParseError at [row,col]:[287271,24]
Message: Invalid byte 1 of 1-byte UTF-8 sequence.
	at com.sun.xml.internal.stream.XMLEventReaderImpl.next(XMLEventReaderImpl.java:252)
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	*__checkpoint ⇢ Body from GET http://localhost:8008/ABCD [DefaultClientResponse]
Original Stack Trace:
		at com.sun.xml.internal.stream.XMLEventReaderImpl.next(XMLEventReaderImpl.java:252)
		at java.util.Iterator.forEachRemaining(Iterator.java:116)
		at org.springframework.http.codec.xml.Jaxb2XmlDecoder.decode(Jaxb2XmlDecoder.java:194)
		at org.springframework.http.codec.xml.Jaxb2XmlDecoder.lambda$decodeToMono$2(Jaxb2XmlDecoder.java:183)
		at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:113)
		at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107)
		at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:295)
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337)
		at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816)
		at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:159)
		at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142)
		at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260)
		at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142)
		at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:400)
		at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:419)
		at reactor.netty.channel.ChannelOperations.terminate(ChannelOperations.java:473)
		at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:703)
		at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:93)
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
		at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
		at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
		at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:327)
		at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:299)
		at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
		at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
		at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
		at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
		at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:722)
		at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:658)
		at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:584)
		at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:496)
		at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
		at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
		at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
		at java.lang.Thread.run(Thread.java:748)

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)type: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions