From 043ec2c8b233b667128269e3af34c747f7af2f62 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Tue, 12 Oct 2010 11:55:29 +0000 Subject: [PATCH] SPR-7636 - XStreamMarshaller - Add support for configuring XStream with aliases by class (type) and also defining multiple attributes for the same class. --- .../oxm/xstream/XStreamMarshaller.java | 83 +++++++++++++++---- .../oxm/xstream/FlightSubclass.java | 24 ++++++ .../oxm/xstream/XStreamMarshallerTests.java | 38 ++++++++- 3 files changed, 130 insertions(+), 15 deletions(-) create mode 100644 org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/FlightSubclass.java diff --git a/org.springframework.oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java b/org.springframework.oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java index 1014066d2b6..d6521ff5dd3 100644 --- a/org.springframework.oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java +++ b/org.springframework.oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java @@ -23,6 +23,8 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLEventWriter; @@ -152,20 +154,48 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin * @see XStream#alias(String, Class) */ public void setAliases(Map aliases) throws ClassNotFoundException { - for (Map.Entry entry : aliases.entrySet()) { - String alias = entry.getKey(); + Map> classMap = toClassMap(aliases); + + for (Map.Entry> entry : classMap.entrySet()) { + this.getXStream().alias(entry.getKey(), entry.getValue()); + } + } + + /** + * Sets the aliases by type map, consisting of string aliases mapped to classes. Any class that is assignable to + * this type will be aliased to the same name. Keys are aliases; values are either + * {@code Class} instances, or String class names. + * + * @see XStream#aliasType(String, Class) + */ + public void setAliasesByType(Map aliases) throws ClassNotFoundException { + Map> classMap = toClassMap(aliases); + + for (Map.Entry> entry : classMap.entrySet()) { + this.getXStream().aliasType(entry.getKey(), entry.getValue()); + } + } + + private Map> toClassMap(Map map) throws ClassNotFoundException { + Map> result = new LinkedHashMap>(map.size()); + + for (Map.Entry entry : map.entrySet()) { + String key = entry.getKey(); Object value = entry.getValue(); Class type; if (value instanceof Class) { type = (Class) value; - } else if (value instanceof String) { + } + else if (value instanceof String) { String s = (String) value; type = ClassUtils.forName(s, classLoader); - } else { + } + else { throw new IllegalArgumentException("Unknown value [" + value + "], expected String or Class"); } - this.getXStream().alias(alias, type); + result.put(key, type); } + return result; } /** @@ -203,22 +233,47 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin /** * Set the types to use XML attributes for. The given map can contain - * either <String, Class> pairs, in which case - * {@link XStream#useAttributeFor(String, Class)} is called, - * or <Class, String> pairs, which results in + * either {@code } pairs, in which case + * {@link XStream#useAttributeFor(String, Class)} is called. + * Alternatively, the map can contain {@code } + * or {@code >} pairs, which results in * {@link XStream#useAttributeFor(Class, String)} calls. */ public void setUseAttributeFor(Map attributes) { for (Map.Entry entry : attributes.entrySet()) { - if (entry.getKey() instanceof String && entry.getValue() instanceof Class) { - this.getXStream().useAttributeFor((String) entry.getKey(), (Class) entry.getValue()); + if (entry.getKey() instanceof String) { + if (entry.getValue() instanceof Class) { + this.getXStream().useAttributeFor((String) entry.getKey(), (Class) entry.getValue()); + } + else { + throw new IllegalArgumentException( + "Invalid argument 'attributes'. 'useAttributesFor' property takes map of ," + + " when using a map key of type String"); + } } - else if (entry.getKey() instanceof Class && entry.getValue() instanceof String) { - this.getXStream().useAttributeFor((Class) entry.getKey(), (String) entry.getValue()); + else if (entry.getKey() instanceof Class) { + Class key = (Class) entry.getKey(); + if (entry.getValue() instanceof String) { + this.getXStream().useAttributeFor(key, (String) entry.getValue()); + } + else if (entry.getValue() instanceof List) { + List list = (List) entry.getValue(); + + for (Object o : list) { + if (o instanceof String) { + this.getXStream().useAttributeFor(key, (String) o); + } + } + } + else { + throw new IllegalArgumentException("Invalid argument 'attributes'. " + + "'useAttributesFor' property takes either or > map," + + " when using a map key of type Class"); + } } else { - throw new IllegalArgumentException("Invalid attribute key and value pair. " + - "'useAttributesFor' property takes either a map or a map"); + throw new IllegalArgumentException("Invalid argument 'attributes. " + + "'useAttributesFor' property takes either a map key of type String or Class"); } } } diff --git a/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/FlightSubclass.java b/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/FlightSubclass.java new file mode 100644 index 00000000000..b4f90ff88a3 --- /dev/null +++ b/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/FlightSubclass.java @@ -0,0 +1,24 @@ +/* + * Copyright 2002-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.oxm.xstream; + +/** + * @author Arjen Poutsma + */ +public class FlightSubclass extends Flight { + +} diff --git a/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java b/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java index a2770ce3164..49821ece292 100644 --- a/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java +++ b/org.springframework.oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java @@ -216,6 +216,42 @@ public class XStreamMarshallerTests { String expected = ""; assertXMLEqual("Marshaller does not use attributes", expected, writer.toString()); } + + @Test + public void useAttributesForClassStringListMap() throws Exception { + marshaller + .setUseAttributeFor(Collections.singletonMap(Flight.class, Collections.singletonList("flightNumber"))); + Writer writer = new StringWriter(); + marshaller.marshal(flight, new StreamResult(writer)); + String expected = ""; + assertXMLEqual("Marshaller does not use attributes", expected, writer.toString()); + } + + @Test + public void aliasesByTypeStringClassMap() throws Exception { + Map> aliases = new HashMap>(); + aliases.put("flight", Flight.class); + FlightSubclass flight = new FlightSubclass(); + flight.setFlightNumber(42); + marshaller.setAliasesByType(aliases); + + Writer writer = new StringWriter(); + marshaller.marshal(flight, new StreamResult(writer)); + assertXMLEqual("Marshaller does not use attributes", EXPECTED_STRING, writer.toString()); + } + + @Test + public void aliasesByTypeStringStringMap() throws Exception { + Map aliases = new HashMap(); + aliases.put("flight", Flight.class.getName()); + FlightSubclass flight = new FlightSubclass(); + flight.setFlightNumber(42); + marshaller.setAliasesByType(aliases); + + Writer writer = new StringWriter(); + marshaller.marshal(flight, new StreamResult(writer)); + assertXMLEqual("Marshaller does not use attributes", EXPECTED_STRING, writer.toString()); + } @Test public void fieldAliases() throws Exception { @@ -288,7 +324,7 @@ public class XStreamMarshallerTests { } @Test - public void testAnnotatedMarshalStreamResultWriter() throws Exception { + public void annotatedMarshalStreamResultWriter() throws Exception { marshaller.setAnnotatedClass(Flight.class); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer);