Merge branch '6.2.x'
This commit is contained in:
commit
1ea8a91b85
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.http.converter.xml;
|
package org.springframework.http.converter.xml;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
|
@ -23,7 +24,10 @@ import jakarta.xml.bind.JAXBContext;
|
||||||
import jakarta.xml.bind.JAXBException;
|
import jakarta.xml.bind.JAXBException;
|
||||||
import jakarta.xml.bind.Marshaller;
|
import jakarta.xml.bind.Marshaller;
|
||||||
import jakarta.xml.bind.Unmarshaller;
|
import jakarta.xml.bind.Unmarshaller;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.converter.HttpMessageConversionException;
|
import org.springframework.http.converter.HttpMessageConversionException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -116,4 +120,19 @@ public abstract class AbstractJaxb2HttpMessageConverter<T> extends AbstractXmlHt
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect the charset from the given {@link HttpHeaders#getContentType()}.
|
||||||
|
* @param httpHeaders the current HTTP headers
|
||||||
|
* @return the charset defined in the content type header, or {@code null} if not found
|
||||||
|
*/
|
||||||
|
protected @Nullable Charset detectCharset(HttpHeaders httpHeaders) {
|
||||||
|
MediaType contentType = httpHeaders.getContentType();
|
||||||
|
if (contentType != null && contentType.getCharset() != null) {
|
||||||
|
return contentType.getCharset();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package org.springframework.http.converter.xml;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
|
@ -148,7 +149,10 @@ public class Jaxb2CollectionHttpMessageConverter<T extends Collection>
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Unmarshaller unmarshaller = createUnmarshaller(elementClass);
|
Unmarshaller unmarshaller = createUnmarshaller(elementClass);
|
||||||
XMLStreamReader streamReader = this.inputFactory.createXMLStreamReader(inputMessage.getBody());
|
Charset detectedCharset = detectCharset(inputMessage.getHeaders());
|
||||||
|
XMLStreamReader streamReader = (detectedCharset != null) ?
|
||||||
|
this.inputFactory.createXMLStreamReader(inputMessage.getBody(), detectedCharset.name()) :
|
||||||
|
this.inputFactory.createXMLStreamReader(inputMessage.getBody());
|
||||||
int event = moveToFirstChildOfRootElement(streamReader);
|
int event = moveToFirstChildOfRootElement(streamReader);
|
||||||
|
|
||||||
while (event != XMLStreamReader.END_DOCUMENT) {
|
while (event != XMLStreamReader.END_DOCUMENT) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.http.converter.xml;
|
package org.springframework.http.converter.xml;
|
||||||
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
import javax.xml.parsers.SAXParser;
|
import javax.xml.parsers.SAXParser;
|
||||||
|
|
@ -134,7 +135,7 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa
|
||||||
@Override
|
@Override
|
||||||
protected Object readFromSource(Class<?> clazz, HttpHeaders headers, Source source) throws Exception {
|
protected Object readFromSource(Class<?> clazz, HttpHeaders headers, Source source) throws Exception {
|
||||||
try {
|
try {
|
||||||
source = processSource(source);
|
source = processSource(source, detectCharset(headers));
|
||||||
Unmarshaller unmarshaller = createUnmarshaller(clazz);
|
Unmarshaller unmarshaller = createUnmarshaller(clazz);
|
||||||
if (clazz.isAnnotationPresent(XmlRootElement.class)) {
|
if (clazz.isAnnotationPresent(XmlRootElement.class)) {
|
||||||
return unmarshaller.unmarshal(source);
|
return unmarshaller.unmarshal(source);
|
||||||
|
|
@ -159,9 +160,12 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Source processSource(Source source) {
|
protected Source processSource(Source source, @Nullable Charset charset) {
|
||||||
if (source instanceof StreamSource streamSource) {
|
if (source instanceof StreamSource streamSource) {
|
||||||
InputSource inputSource = new InputSource(streamSource.getInputStream());
|
InputSource inputSource = new InputSource(streamSource.getInputStream());
|
||||||
|
if (charset != null) {
|
||||||
|
inputSource.setEncoding(charset.name());
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// By default, Spring will prevent the processing of external entities.
|
// By default, Spring will prevent the processing of external entities.
|
||||||
// This is a mitigation against XXE attacks.
|
// This is a mitigation against XXE attacks.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -35,6 +35,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.core.ParameterizedTypeReference;
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||||
import org.springframework.web.testfixture.http.MockHttpInputMessage;
|
import org.springframework.web.testfixture.http.MockHttpInputMessage;
|
||||||
|
|
||||||
|
|
@ -204,6 +205,18 @@ class Jaxb2CollectionHttpMessageConverterTests {
|
||||||
.withMessageContaining("\"lol9\"");
|
.withMessageContaining("\"lol9\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void readXmlRootElementListHeaderCharset() throws Exception {
|
||||||
|
String content = "<list><rootElement><type s=\"Hellø Wørld\"/></rootElement></list>";
|
||||||
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(content.getBytes(StandardCharsets.ISO_8859_1));
|
||||||
|
inputMessage.getHeaders().setContentType(MediaType.parseMediaType("application/xml;charset=iso-8859-1"));
|
||||||
|
List<RootElement> result = (List<RootElement>) converter.read(rootElementListType, null, inputMessage);
|
||||||
|
|
||||||
|
assertThat(result).as("Invalid result").hasSize(1);
|
||||||
|
assertThat(result.get(0).type.s).as("Invalid result").isEqualTo("Hellø Wørld");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@XmlRootElement
|
@XmlRootElement
|
||||||
public static class RootElement {
|
public static class RootElement {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -180,6 +180,15 @@ class Jaxb2RootElementHttpMessageConverterTests {
|
||||||
.withMessageContaining("DOCTYPE");
|
.withMessageContaining("DOCTYPE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void readXmlRootElementHeaderCharset() throws Exception {
|
||||||
|
byte[] body = "<rootElement><type s=\"Hellø Wørld\"/></rootElement>".getBytes(StandardCharsets.ISO_8859_1);
|
||||||
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body);
|
||||||
|
inputMessage.getHeaders().setContentType(MediaType.parseMediaType("application/xml;charset=iso-8859-1"));
|
||||||
|
RootElement result = (RootElement) converter.read(RootElement.class, inputMessage);
|
||||||
|
assertThat(result.type.s).as("Invalid result").isEqualTo("Hellø Wørld");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void writeXmlRootElement() throws Exception {
|
void writeXmlRootElement() throws Exception {
|
||||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue