SPR-5838 - XstreamMarshaller does not support field aliasing

This commit is contained in:
Arjen Poutsma 2009-06-19 13:51:59 +00:00
parent bb3c9c1898
commit c2e65baa70
2 changed files with 62 additions and 9 deletions

View File

@ -58,6 +58,7 @@ import org.xml.sax.XMLReader;
import org.xml.sax.ext.LexicalHandler; import org.xml.sax.ext.LexicalHandler;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.oxm.MarshallingFailureException; import org.springframework.oxm.MarshallingFailureException;
import org.springframework.oxm.UncategorizedMappingException; import org.springframework.oxm.UncategorizedMappingException;
import org.springframework.oxm.UnmarshallingFailureException; import org.springframework.oxm.UnmarshallingFailureException;
@ -66,6 +67,7 @@ import org.springframework.oxm.support.AbstractMarshaller;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.xml.StaxUtils; import org.springframework.util.xml.StaxUtils;
/** /**
@ -88,7 +90,7 @@ import org.springframework.util.xml.StaxUtils;
* @see #setConverters * @see #setConverters
* @see #setEncoding * @see #setEncoding
*/ */
public class XStreamMarshaller extends AbstractMarshaller implements InitializingBean { public class XStreamMarshaller extends AbstractMarshaller implements InitializingBean, BeanClassLoaderAware {
/** /**
* The default encoding used for stream access: UTF-8. * The default encoding used for stream access: UTF-8.
@ -104,6 +106,8 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
private Class[] supportedClasses; private Class[] supportedClasses;
private ClassLoader classLoader;
/** /**
* Returns the XStream instance used by this marshaller. * Returns the XStream instance used by this marshaller.
*/ */
@ -142,11 +146,48 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
} }
/** /**
* Set an alias/type map, consisting of string aliases mapped to <code>Class</code> instances. * Sets an alias/type map, consisting of string aliases mapped to classes. Keys are aliases; values are either
* {@code Class} instances, or String class names.
*
* @see XStream#alias(String, Class)
*/ */
public void setAliases(Map<String, Class> aliases) { public void setAliases(Map<String, ?> aliases) throws ClassNotFoundException {
for (Map.Entry<String, Class> entry : aliases.entrySet()) { for (Map.Entry<String, ?> entry : aliases.entrySet()) {
this.getXStream().alias(entry.getKey(), entry.getValue()); String alias = entry.getKey();
Object value = entry.getValue();
Class type;
if (value instanceof Class) {
type = (Class) value;
} else if (value instanceof String) {
String s = (String) value;
type = ClassUtils.forName(s, classLoader);
} else {
throw new IllegalArgumentException("Unknown value [" + value + "], expected String or Class");
}
this.getXStream().alias(alias, type);
}
}
/**
* Sets a field alias/type map, consiting of field names
* @param aliases
* @throws ClassNotFoundException
* @throws NoSuchFieldException
* @see XStream#aliasField(String, Class, String)
*/
public void setFieldAliases(Map<String, String> aliases) throws ClassNotFoundException, NoSuchFieldException {
for (Map.Entry<String, String> entry : aliases.entrySet()) {
String alias = entry.getValue();
String field = entry.getKey();
int idx = field.lastIndexOf('.');
if (idx != -1) {
String className = field.substring(0, idx);
Class clazz = ClassUtils.forName(className, classLoader);
String fieldName = field.substring(idx + 1);
this.getXStream().aliasField(alias, clazz, fieldName);
} else {
throw new IllegalArgumentException("Field name [" + field + "] does not contain '.'");
}
} }
} }
@ -212,7 +253,7 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
/** /**
* Set the classes for which mappings will be read from class-level JDK 1.5+ annotation metadata. * Set the classes for which mappings will be read from class-level JDK 1.5+ annotation metadata.
* @see com.thoughtworks.xstream.XStream#processAnnotations(Class) * @see XStream#processAnnotations(Class)
*/ */
public void setAnnotatedClass(Class<?> annotatedClass) { public void setAnnotatedClass(Class<?> annotatedClass) {
Assert.notNull(annotatedClass, "'annotatedClass' must not be null"); Assert.notNull(annotatedClass, "'annotatedClass' must not be null");
@ -221,7 +262,7 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
/** /**
* Set annotated classes for which aliases will be read from class-level JDK 1.5+ annotation metadata. * Set annotated classes for which aliases will be read from class-level JDK 1.5+ annotation metadata.
* @see com.thoughtworks.xstream.XStream#processAnnotations(Class[]) * @see XStream#processAnnotations(Class[])
*/ */
public void setAnnotatedClasses(Class<?>[] annotatedClasses) { public void setAnnotatedClasses(Class<?>[] annotatedClasses) {
Assert.notEmpty(annotatedClasses, "'annotatedClasses' must not be empty"); Assert.notEmpty(annotatedClasses, "'annotatedClasses' must not be empty");
@ -256,6 +297,10 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
customizeXStream(getXStream()); customizeXStream(getXStream());
} }
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
/** /**
* Template to allow for customizing of the given {@link XStream}. * Template to allow for customizing of the given {@link XStream}.
* <p>Default implementation is empty. * <p>Default implementation is empty.

View File

@ -68,8 +68,8 @@ public class XStreamMarshallerTests {
@Before @Before
public void createMarshaller() throws Exception { public void createMarshaller() throws Exception {
marshaller = new XStreamMarshaller(); marshaller = new XStreamMarshaller();
Map<String, Class> aliases = new HashMap<String, Class>(); Map<String, String> aliases = new HashMap<String, String>();
aliases.put("flight", Flight.class); aliases.put("flight", Flight.class.getName());
marshaller.setAliases(aliases); marshaller.setAliases(aliases);
flight = new Flight(); flight = new Flight();
flight.setFlightNumber(42L); flight.setFlightNumber(42L);
@ -214,6 +214,14 @@ public class XStreamMarshallerTests {
assertXMLEqual("Marshaller does not use attributes", expected, writer.toString()); assertXMLEqual("Marshaller does not use attributes", expected, writer.toString());
} }
@Test
public void fieldAliases() throws Exception {
marshaller.setFieldAliases(Collections.singletonMap("org.springframework.oxm.xstream.Flight.flightNumber", "flightNo"));
Writer writer = new StringWriter();
marshaller.marshal(flight, new StreamResult(writer));
String expected = "<flight><flightNo>42</flightNo></flight>";
assertXMLEqual("Marshaller does not use aliases", expected, writer.toString());
}
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")