Log Message
Implement support for aliasing JavaClassConverter, JavaFieldConverter and JavaMethodConverter (XSTR-578).
Modified Paths
- trunk/xstream/src/java/com/thoughtworks/xstream/converters/extended/JavaClassConverter.java
- trunk/xstream/src/java/com/thoughtworks/xstream/converters/extended/JavaFieldConverter.java
- trunk/xstream/src/java/com/thoughtworks/xstream/converters/extended/JavaMethodConverter.java
- trunk/xstream/src/test/com/thoughtworks/acceptance/AliasTest.java
- trunk/xstream-distribution/src/content/changes.html
- trunk/xstream-distribution/src/content/faq.html
Diff
Modified: trunk/xstream/src/java/com/thoughtworks/xstream/converters/extended/JavaClassConverter.java (2096 => 2097)
--- trunk/xstream/src/java/com/thoughtworks/xstream/converters/extended/JavaClassConverter.java 2013-07-08 22:36:06 UTC (rev 2096)
+++ trunk/xstream/src/java/com/thoughtworks/xstream/converters/extended/JavaClassConverter.java 2013-07-12 16:45:09 UTC (rev 2097)
@@ -36,7 +36,7 @@
* @since upcoming
*/
public JavaClassConverter(ClassLoaderReference classLoaderReference) {
- mapper = new DefaultMapper(classLoaderReference);
+ this(new DefaultMapper(classLoaderReference));
}
/**
@@ -46,12 +46,22 @@
this(new ClassLoaderReference(classLoader));
}
+ /**
+ * Construct a JavaClassConverter that uses a provided mapper. Depending on the mapper
+ * chain it will not only be used to load classes, but also to support type aliases.
+ * @param mapper to use
+ * @since upcoming
+ */
+ protected JavaClassConverter(Mapper mapper) {
+ this.mapper = mapper;
+ }
+
public boolean canConvert(Class clazz) {
return Class.class.equals(clazz); // :)
}
public String toString(Object obj) {
- return ((Class) obj).getName();
+ return mapper.serializedClass(((Class) obj));
}
public Object fromString(String str) {
Modified: trunk/xstream/src/java/com/thoughtworks/xstream/converters/extended/JavaFieldConverter.java (2096 => 2097)
--- trunk/xstream/src/java/com/thoughtworks/xstream/converters/extended/JavaFieldConverter.java 2013-07-08 22:36:06 UTC (rev 2096)
+++ trunk/xstream/src/java/com/thoughtworks/xstream/converters/extended/JavaFieldConverter.java 2013-07-12 16:45:09 UTC (rev 2097)
@@ -18,6 +18,8 @@
import com.thoughtworks.xstream.core.ClassLoaderReference;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import com.thoughtworks.xstream.mapper.DefaultMapper;
+import com.thoughtworks.xstream.mapper.Mapper;
import java.lang.reflect.Field;
@@ -29,6 +31,7 @@
public class JavaFieldConverter implements Converter {
private final SingleValueConverter javaClassConverter;
+ private final Mapper mapper;
/**
* Construct a JavaFieldConverter.
@@ -36,7 +39,7 @@
* @since upcoming
*/
public JavaFieldConverter(ClassLoaderReference classLoaderReference) {
- this.javaClassConverter = new JavaClassConverter(classLoaderReference);
+ this(new JavaClassConverter(classLoaderReference), new DefaultMapper(classLoaderReference));
}
/**
@@ -46,19 +49,31 @@
this(new ClassLoaderReference(classLoader));
}
+ /**
+ * Construct a JavaFieldConverter. Depending on the mapper chain the converter will also respect aliases.
+ * @param javaClassConverter the converter to use
+ * @param mapper to use
+ * @since upcoming
+ */
+ protected JavaFieldConverter(SingleValueConverter javaClassConverter, Mapper mapper) {
+ this.javaClassConverter = javaClassConverter;
+ this.mapper = mapper;
+ }
+
public boolean canConvert(Class type) {
return type.equals(Field.class);
}
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
Field field = (Field) source;
+ Class type = field.getDeclaringClass();
writer.startNode("name");
- writer.setValue(field.getName());
+ writer.setValue(mapper.serializedMember(type, field.getName()));
writer.endNode();
writer.startNode("clazz");
- writer.setValue(javaClassConverter.toString(field.getDeclaringClass()));
+ writer.setValue(javaClassConverter.toString(type));
writer.endNode();
}
@@ -79,7 +94,7 @@
Class declaringClass = (Class)javaClassConverter.fromString(declaringClassName);
try {
- return declaringClass.getDeclaredField(methodName);
+ return declaringClass.getDeclaredField(mapper.realMember(declaringClass, methodName));
} catch (NoSuchFieldException e) {
throw new ConversionException(e);
}
Modified: trunk/xstream/src/java/com/thoughtworks/xstream/converters/extended/JavaMethodConverter.java (2096 => 2097)
--- trunk/xstream/src/java/com/thoughtworks/xstream/converters/extended/JavaMethodConverter.java 2013-07-08 22:36:06 UTC (rev 2096)
+++ trunk/xstream/src/java/com/thoughtworks/xstream/converters/extended/JavaMethodConverter.java 2013-07-12 16:45:09 UTC (rev 2097)
@@ -41,7 +41,7 @@
* @since upcoming
*/
public JavaMethodConverter(ClassLoaderReference classLoaderReference) {
- this.javaClassConverter = new JavaClassConverter(classLoaderReference);
+ this(new JavaClassConverter(classLoaderReference));
}
/**
@@ -51,6 +51,15 @@
this(new ClassLoaderReference(classLoader));
}
+ /**
+ * Construct a JavaMethodConverter.
+ * @param javaClassConverter the converter to use
+ * @since upcoming
+ */
+ protected JavaMethodConverter(SingleValueConverter javaClassConverter) {
+ this.javaClassConverter = javaClassConverter;
+ }
+
public boolean canConvert(Class type) {
return type.equals(Method.class) || type.equals(Constructor.class);
}
Modified: trunk/xstream/src/test/com/thoughtworks/acceptance/AliasTest.java (2096 => 2097)
--- trunk/xstream/src/test/com/thoughtworks/acceptance/AliasTest.java 2013-07-08 22:36:06 UTC (rev 2096)
+++ trunk/xstream/src/test/com/thoughtworks/acceptance/AliasTest.java 2013-07-12 16:45:09 UTC (rev 2097)
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2004, 2005, 2006 Joe Walnes.
- * Copyright (C) 2006, 2007, 2008, 2009 XStream Committers.
+ * Copyright (C) 2006, 2007, 2008, 2009, 2013 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
@@ -12,13 +12,22 @@
package com.thoughtworks.acceptance;
import com.thoughtworks.acceptance.objects.Category;
+import com.thoughtworks.acceptance.objects.Product;
import com.thoughtworks.acceptance.objects.Software;
import com.thoughtworks.acceptance.someobjects.WithList;
import com.thoughtworks.acceptance.someobjects.X;
import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.converters.SingleValueConverter;
+import com.thoughtworks.xstream.converters.extended.JavaClassConverter;
+import com.thoughtworks.xstream.converters.extended.JavaFieldConverter;
+import com.thoughtworks.xstream.converters.extended.JavaMethodConverter;
+import com.thoughtworks.xstream.core.util.Primitives;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import com.thoughtworks.xstream.io.xml.XppDriver;
+import com.thoughtworks.xstream.mapper.ArrayMapper;
import com.thoughtworks.xstream.mapper.CannotResolveClassException;
+import com.thoughtworks.xstream.mapper.Mapper;
+import com.thoughtworks.xstream.mapper.MapperWrapper;
import java.util.ArrayList;
import java.util.LinkedList;
@@ -369,4 +378,62 @@
assertBothWays(withList, xml);
}
+
+ private void takingDoubles(Double d1, double d2) {}
+
+ public void testCanCreateAliasingJavaTypeConverter() throws NoSuchFieldException, NoSuchMethodException {
+ Mapper mapper = new MapperWrapper(xstream.getMapper().lookupMapperOfType(ArrayMapper.class)) {
+ public Class realClass(String elementName) {
+ Class primitiveType = Primitives.primitiveType(elementName);
+ return primitiveType != null ? primitiveType : super.realClass(elementName);
+ }
+ };
+ SingleValueConverter javaClassConverter = new JavaClassConverter(mapper) {};
+ xstream.registerConverter(javaClassConverter);
+ xstream.registerConverter(new JavaMethodConverter(javaClassConverter){});
+ xstream.registerConverter(new JavaFieldConverter(javaClassConverter, mapper){});
+ xstream.alias("A", TypeA.class);
+ xstream.alias("Prod", Product.class);
+ xstream.aliasField("a", TypeA.class, "attrA");
+ xstream.alias("Test", getClass());
+
+ List list = new ArrayList();
+ list.add(TypeA.class);
+ list.add(int[][][].class);
+ list.add(Integer[][][].class);
+ list.add(TypeA.class.getDeclaredField("attrA"));
+ list.add(Product.class.getConstructor(new Class[]{String.class, String.class, double.class}));
+ list.add(getClass().getDeclaredMethod("takingDoubles", new Class[]{Double.class, double.class}));
+ list.add(ArrayList.class);
+
+ String xml = "" //
+ + "<list>\n"
+ + " <java-class>A</java-class>\n"
+ + " <java-class>int-array-array-array</java-class>\n"
+ + " <java-class>java.lang.Integer-array-array-array</java-class>\n"
+ + " <field>\n"
+ + " <name>a</name>\n"
+ + " <clazz>A</clazz>\n"
+ + " </field>\n"
+ + " <constructor>\n"
+ + " <class>Prod</class>\n"
+ + " <parameter-types>\n"
+ + " <class>string</class>\n"
+ + " <class>string</class>\n"
+ + " <class>double</class>\n"
+ + " </parameter-types>\n"
+ + " </constructor>\n"
+ + " <method>\n"
+ + " <class>Test</class>\n"
+ + " <name>takingDoubles</name>\n"
+ + " <parameter-types>\n"
+ + " <class>java.lang.Double</class>\n"
+ + " <class>double</class>\n"
+ + " </parameter-types>\n"
+ + " </method>\n"
+ + " <java-class>java.util.ArrayList</java-class>\n"
+ + "</list>";
+
+ assertBothWays(list, xml);
+ }
}
Modified: trunk/xstream-distribution/src/content/changes.html (2096 => 2097)
--- trunk/xstream-distribution/src/content/changes.html 2013-07-08 22:36:06 UTC (rev 2096)
+++ trunk/xstream-distribution/src/content/changes.html 2013-07-12 16:45:09 UTC (rev 2097)
@@ -75,6 +75,9 @@
map with the nearest matching element type.</li>
<li>JIRA:XSTR-740: ISO8601GregorianCalendarConverter creates Calendar instance with wrong Locale in Java 7 if
the Locale for the LocaleCategory.FORMAT is different to the global default Locale.</li>
+ <li>JIRA:XSTR-578: Implement support for aliasing in JavaClasConverter, JavaFieldConverter and
+ JavaMethodConverter. While it is not possible to enable this in general, new constructors have been added to
+ these converters and an example in the acceptance tests (AliasTest).</li>
</ul>
<h2>API changes</h2>
Modified: trunk/xstream-distribution/src/content/faq.html (2096 => 2097)
--- trunk/xstream-distribution/src/content/faq.html 2013-07-08 22:36:06 UTC (rev 2096)
+++ trunk/xstream-distribution/src/content/faq.html 2013-07-12 16:45:09 UTC (rev 2097)
@@ -1,7 +1,7 @@
<html>
<!--
Copyright (C) 2005, 2006 Joe Walnes.
- Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 XStream committers.
+ Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 XStream committers.
All rights reserved.
The software in this package is published under the terms of the BSD
@@ -228,6 +228,26 @@
<p>Note, that it is possible to configure XStream to omit the container element <i>toys</i> using implicit collections.</p>
<!-- ...................................................... -->
+ <h2 id="Serialization_reflection_types">Why do serialized types, fields or methods do not use aliasing for the names?</h2>
+
+ <p>XStream normally has no to separate between a primitive and its boxed type. The complete reflection API works
+ always with the boxed types and converts to primitives types on the fly. However, for method and field type
+ signatures the difference is essential. Nevertheless it is possible to register derived versions of the converters
+ that are able to respect the aliasing with some minor effort. Following lines are taken from the AliasTest in the
+ acceptence tests:</p>
+<div class="Source Java"><pre>XStream xstream = new XStream();
+Mapper mapper = new MapperWrapper(xstream.getMapper().lookupMapperOfType(ArrayMapper.class)) {
+ public Class realClass(String elementName) {
+ Class primitiveType = Primitives.primitiveType(elementName);
+ return primitiveType != null ? primitiveType : super.realClass(elementName);
+ }
+};
+SingleValueConverter javaClassConverter = new JavaClassConverter(mapper) {};
+xstream.registerConverter(javaClassConverter);
+xstream.registerConverter(new JavaMethodConverter(javaClassConverter){});
+xstream.registerConverter(new JavaFieldConverter(javaClassConverter, mapper){});</pre></div>
+
+ <!-- ...................................................... -->
<h2 id="Serialization_implicit_null">My implicit collection is suddenly null after deserialization instead of empty!</h2>
<p>By declaring a collection as implicit, the result will have no direct representation of the collection container
To unsubscribe from this list please visit:
