Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Charset for input stream ignored in Jaxb2XmlDecoder #28599

Closed
mo-zachery-harley opened this issue Jun 9, 2022 · 0 comments
Closed

Charset for input stream ignored in Jaxb2XmlDecoder #28599

mo-zachery-harley opened this issue Jun 9, 2022 · 0 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: bug A general bug
Milestone

Comments

@mo-zachery-harley
Copy link

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)
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jun 9, 2022
@sbrannen sbrannen changed the title Charset ignored on XML Event Reader Charset for input stream ignored in Jaxb2XmlDecoder Jun 9, 2022
@sbrannen sbrannen added in: web Issues in web modules (web, webmvc, webflux, websocket) type: bug A general bug labels Jun 9, 2022
@sbrannen sbrannen added this to the Triage Queue milestone Jun 9, 2022
@poutsma poutsma self-assigned this Jun 13, 2022
@poutsma poutsma modified the milestones: Triage Queue, 5.3.21 Jun 13, 2022
@poutsma poutsma removed the status: waiting-for-triage An issue we've not yet triaged or decided on label Jun 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

4 participants