Skip to content

Commit 1ea8a91

Browse files
committed
Merge branch '6.2.x'
2 parents e02e67b + 2af0323 commit 1ea8a91

File tree

5 files changed

+55
-6
lines changed

5 files changed

+55
-6
lines changed

spring-web/src/main/java/org/springframework/http/converter/xml/AbstractJaxb2HttpMessageConverter.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,18 @@
1616

1717
package org.springframework.http.converter.xml;
1818

19+
import java.nio.charset.Charset;
1920
import java.util.concurrent.ConcurrentHashMap;
2021
import java.util.concurrent.ConcurrentMap;
2122

2223
import jakarta.xml.bind.JAXBContext;
2324
import jakarta.xml.bind.JAXBException;
2425
import jakarta.xml.bind.Marshaller;
2526
import jakarta.xml.bind.Unmarshaller;
27+
import org.jspecify.annotations.Nullable;
2628

29+
import org.springframework.http.HttpHeaders;
30+
import org.springframework.http.MediaType;
2731
import org.springframework.http.converter.HttpMessageConversionException;
2832

2933
/**
@@ -116,4 +120,19 @@ protected final JAXBContext getJaxbContext(Class<?> clazz) {
116120
});
117121
}
118122

123+
/**
124+
* Detect the charset from the given {@link HttpHeaders#getContentType()}.
125+
* @param httpHeaders the current HTTP headers
126+
* @return the charset defined in the content type header, or {@code null} if not found
127+
*/
128+
protected @Nullable Charset detectCharset(HttpHeaders httpHeaders) {
129+
MediaType contentType = httpHeaders.getContentType();
130+
if (contentType != null && contentType.getCharset() != null) {
131+
return contentType.getCharset();
132+
}
133+
else {
134+
return null;
135+
}
136+
}
137+
119138
}

spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverter.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.io.IOException;
2020
import java.lang.reflect.ParameterizedType;
2121
import java.lang.reflect.Type;
22+
import java.nio.charset.Charset;
2223
import java.util.ArrayList;
2324
import java.util.Collection;
2425
import java.util.LinkedHashSet;
@@ -148,7 +149,10 @@ public T read(Type type, @Nullable Class<?> contextClass, HttpInputMessage input
148149

149150
try {
150151
Unmarshaller unmarshaller = createUnmarshaller(elementClass);
151-
XMLStreamReader streamReader = this.inputFactory.createXMLStreamReader(inputMessage.getBody());
152+
Charset detectedCharset = detectCharset(inputMessage.getHeaders());
153+
XMLStreamReader streamReader = (detectedCharset != null) ?
154+
this.inputFactory.createXMLStreamReader(inputMessage.getBody(), detectedCharset.name()) :
155+
this.inputFactory.createXMLStreamReader(inputMessage.getBody());
152156
int event = moveToFirstChildOfRootElement(streamReader);
153157

154158
while (event != XMLStreamReader.END_DOCUMENT) {

spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
1717
package org.springframework.http.converter.xml;
1818

1919
import java.io.StringReader;
20+
import java.nio.charset.Charset;
2021

2122
import javax.xml.parsers.ParserConfigurationException;
2223
import javax.xml.parsers.SAXParser;
@@ -134,7 +135,7 @@ protected boolean supports(Class<?> clazz) {
134135
@Override
135136
protected Object readFromSource(Class<?> clazz, HttpHeaders headers, Source source) throws Exception {
136137
try {
137-
source = processSource(source);
138+
source = processSource(source, detectCharset(headers));
138139
Unmarshaller unmarshaller = createUnmarshaller(clazz);
139140
if (clazz.isAnnotationPresent(XmlRootElement.class)) {
140141
return unmarshaller.unmarshal(source);
@@ -159,9 +160,12 @@ protected Object readFromSource(Class<?> clazz, HttpHeaders headers, Source sour
159160
}
160161
}
161162

162-
protected Source processSource(Source source) {
163+
protected Source processSource(Source source, @Nullable Charset charset) {
163164
if (source instanceof StreamSource streamSource) {
164165
InputSource inputSource = new InputSource(streamSource.getInputStream());
166+
if (charset != null) {
167+
inputSource.setEncoding(charset.name());
168+
}
165169
try {
166170
// By default, Spring will prevent the processing of external entities.
167171
// This is a mitigation against XXE attacks.

spring-web/src/test/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverterTests.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
3535
import org.springframework.core.ParameterizedTypeReference;
3636
import org.springframework.core.io.ClassPathResource;
3737
import org.springframework.core.io.Resource;
38+
import org.springframework.http.MediaType;
3839
import org.springframework.http.converter.HttpMessageNotReadableException;
3940
import org.springframework.web.testfixture.http.MockHttpInputMessage;
4041

@@ -204,6 +205,18 @@ void testXmlBomb() {
204205
.withMessageContaining("\"lol9\"");
205206
}
206207

208+
@Test
209+
@SuppressWarnings("unchecked")
210+
public void readXmlRootElementListHeaderCharset() throws Exception {
211+
String content = "<list><rootElement><type s=\"Hellø Wørld\"/></rootElement></list>";
212+
MockHttpInputMessage inputMessage = new MockHttpInputMessage(content.getBytes(StandardCharsets.ISO_8859_1));
213+
inputMessage.getHeaders().setContentType(MediaType.parseMediaType("application/xml;charset=iso-8859-1"));
214+
List<RootElement> result = (List<RootElement>) converter.read(rootElementListType, null, inputMessage);
215+
216+
assertThat(result).as("Invalid result").hasSize(1);
217+
assertThat(result.get(0).type.s).as("Invalid result").isEqualTo("Hellø Wørld");
218+
}
219+
207220

208221
@XmlRootElement
209222
public static class RootElement {

spring-web/src/test/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverterTests.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -180,6 +180,15 @@ void testXmlBomb() throws Exception {
180180
.withMessageContaining("DOCTYPE");
181181
}
182182

183+
@Test
184+
void readXmlRootElementHeaderCharset() throws Exception {
185+
byte[] body = "<rootElement><type s=\"Hellø Wørld\"/></rootElement>".getBytes(StandardCharsets.ISO_8859_1);
186+
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body);
187+
inputMessage.getHeaders().setContentType(MediaType.parseMediaType("application/xml;charset=iso-8859-1"));
188+
RootElement result = (RootElement) converter.read(RootElement.class, inputMessage);
189+
assertThat(result.type.s).as("Invalid result").isEqualTo("Hellø Wørld");
190+
}
191+
183192
@Test
184193
void writeXmlRootElement() throws Exception {
185194
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();

0 commit comments

Comments
 (0)