Add marshalling hooks in Jaxb2RootElementHttpMessageConverter

Allow Jaxb2RootElementHttpMessageConverter subclasses to customize
the {@link Marshaller} and the {@link Unmarshaller} created by the
message converter.

Issue: SPR-11488
This commit is contained in:
Sebastien Deleuze 2014-02-28 17:04:15 +01:00 committed by Rossen Stoyanchev
parent 474142a862
commit 45be8c0692
2 changed files with 135 additions and 5 deletions

View File

@ -28,7 +28,6 @@ import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;
@ -39,7 +38,6 @@ import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.ClassUtils;
import org.springframework.util.xml.StaxUtils;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
@ -53,6 +51,7 @@ import org.xml.sax.helpers.XMLReaderFactory;
* annotated with with {@link XmlRootElement}, or subclasses thereof.
*
* @author Arjen Poutsma
* @author Sebastien Deleuze
* @since 3.0
*/
public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessageConverter<Object> {
@ -90,6 +89,7 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa
try {
source = processSource(source);
Unmarshaller unmarshaller = createUnmarshaller(clazz);
this.customizeUnmarshaller(unmarshaller);
if (clazz.isAnnotationPresent(XmlRootElement.class)) {
return unmarshaller.unmarshal(source);
}
@ -132,6 +132,7 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa
try {
Class<?> clazz = ClassUtils.getUserClass(o);
Marshaller marshaller = createMarshaller(clazz);
this.customizeMarshaller(marshaller);
setCharset(headers.getContentType(), marshaller);
marshaller.marshal(o, result);
}
@ -149,4 +150,26 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa
}
}
/**
* Customize the {@link Marshaller} created by this
* message converter before using it to write the object to the output.
* @param marshaller the marshaller to customize
* @see #createMarshaller(Class)
* @since 4.0.3
*/
protected void customizeMarshaller(Marshaller marshaller) {
}
/**
* Customize the {@link Unmarshaller} created by this
* message converter before using it to read the object from the input.
* @param unmarshaller the unmarshaller to customize
* @see #createUnmarshaller(Class)
* @since 4.0.3
*/
protected void customizeUnmarshaller(Unmarshaller unmarshaller) {
}
}

View File

@ -17,15 +17,20 @@
package org.springframework.http.converter.xml;
import java.nio.charset.Charset;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import static org.custommonkey.xmlunit.XMLAssert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
@ -37,10 +42,13 @@ import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.MockHttpInputMessage;
import org.springframework.http.MockHttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.xml.sax.SAXParseException;
/** @author Arjen Poutsma */
/**
* Tests for {@link Jaxb2RootElementHttpMessageConverter}.
*
* @author Arjen Poutsma
* @author Sebastien Deleuze
*/
public class Jaxb2RootElementHttpMessageConverterTests {
private Jaxb2RootElementHttpMessageConverter converter;
@ -146,6 +154,25 @@ public class Jaxb2RootElementHttpMessageConverterTests {
outputMessage.getBodyAsString(Charset.forName("UTF-8")));
}
@Test
public void customizeMarshaller() throws Exception {
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
MyJaxb2RootElementHttpMessageConverter myConverter = new MyJaxb2RootElementHttpMessageConverter();
myConverter.write(new MyRootElement(new MyCustomElement("a", "b")), null, outputMessage);
assertXMLEqual("Invalid result", "<myRootElement><element>a|||b</element></myRootElement>",
outputMessage.getBodyAsString(Charset.forName("UTF-8")));
}
@Test
public void customizeUnmarshaller() throws Exception {
byte[] body = "<myRootElement><element>a|||b</element></myRootElement>".getBytes("UTF-8");
MyJaxb2RootElementHttpMessageConverter myConverter = new MyJaxb2RootElementHttpMessageConverter();
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body);
MyRootElement result = (MyRootElement) myConverter.read(MyRootElement.class, inputMessage);
assertEquals("a", result.getElement().getField1());
assertEquals("b", result.getElement().getField2());
}
@XmlRootElement
public static class RootElement {
@ -176,4 +203,84 @@ public class Jaxb2RootElementHttpMessageConverterTests {
}
public static class MyJaxb2RootElementHttpMessageConverter extends Jaxb2RootElementHttpMessageConverter {
@Override
protected void customizeMarshaller(Marshaller marshaller) {
marshaller.setAdapter(new MyCustomElementAdapter());
}
@Override
protected void customizeUnmarshaller(Unmarshaller unmarshaller) {
unmarshaller.setAdapter(new MyCustomElementAdapter());
}
}
public static class MyCustomElement {
private String field1;
private String field2;
public MyCustomElement() {
}
public MyCustomElement(String field1, String field2) {
this.field1 = field1;
this.field2 = field2;
}
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
}
@XmlRootElement
public static class MyRootElement {
private MyCustomElement element;
public MyRootElement() {
}
public MyRootElement(MyCustomElement element) {
this.element = element;
}
@XmlJavaTypeAdapter(MyCustomElementAdapter.class)
public MyCustomElement getElement() {
return element;
}
public void setElement(MyCustomElement element) {
this.element = element;
}
}
public static class MyCustomElementAdapter extends XmlAdapter<String, MyCustomElement> {
@Override
public String marshal(MyCustomElement c) throws Exception {
return c.getField1() + "|||" + c.getField2();
}
@Override
public MyCustomElement unmarshal(String c) throws Exception {
String[] t = c.split("\\|\\|\\|");
return new MyCustomElement(t[0], t[1]);
}
}
}