org.springframework.oxm.xstream.XStreamMarshaller Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spring-oxm Show documentation
Show all versions of spring-oxm Show documentation
Spring Object/XML Mapping abstraction
/*
* Copyright 2006 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;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.Iterator;
import java.util.Map;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.ConverterMatcher;
import com.thoughtworks.xstream.converters.SingleValueConverter;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.CompactWriter;
import com.thoughtworks.xstream.io.xml.DomReader;
import com.thoughtworks.xstream.io.xml.DomWriter;
import com.thoughtworks.xstream.io.xml.QNameMap;
import com.thoughtworks.xstream.io.xml.SaxWriter;
import com.thoughtworks.xstream.io.xml.StaxReader;
import com.thoughtworks.xstream.io.xml.StaxWriter;
import com.thoughtworks.xstream.io.xml.XppReader;
import org.springframework.beans.propertyeditors.ClassEditor;
import org.springframework.oxm.AbstractMarshaller;
import org.springframework.oxm.XmlMappingException;
import org.springframework.xml.stream.StaxEventContentHandler;
import org.springframework.xml.stream.XmlEventStreamReader;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.LexicalHandler;
/**
* Implementation of the Marshaller
interface for XStream. By default, XStream does not require any further
* configuration, though class aliases can be used to have more control over the behavior of XStream.
*
* Due to XStream's API, it is required to set the encoding used for writing to outputstreams. It defaults to
* UTF-8
.
*
* Note that XStream is an XML serialization library, not a data binding library. Therefore, it has limited
* namespace support. As such, it is rather unsuitable for usage within Web services.
*
* @author Peter Meijer
* @author Arjen Poutsma
* @see #setEncoding(String)
* @see #DEFAULT_ENCODING
* @see #setAliases(Map)
* @see #setConverters(ConverterMatcher[])
* @since 1.0.0
*/
public class XStreamMarshaller extends AbstractMarshaller {
/** The default encoding used for stream access. */
public static final String DEFAULT_ENCODING = "UTF-8";
private XStream xstream = new XStream();
private String encoding;
/**
* Returns the encoding to be used for stream access. If this property is not set, the default encoding is used.
*
* @see #DEFAULT_ENCODING
*/
public String getEncoding() {
return encoding != null ? encoding : DEFAULT_ENCODING;
}
/**
* Sets the encoding to be used for stream access. If this property is not set, the default encoding is used.
*
* @see #DEFAULT_ENCODING
*/
public void setEncoding(String encoding) {
this.encoding = encoding;
}
/**
* Sets the XStream mode.
*
* @see XStream#XPATH_REFERENCES
* @see XStream#ID_REFERENCES
* @see XStream#NO_REFERENCES
*/
public void setMode(int mode) {
xstream.setMode(mode);
}
/**
* Sets the Converters
or SingleValueConverters
to be registered with the
* XStream
instance.
*
* @see Converter
* @see SingleValueConverter
*/
public void setConverters(ConverterMatcher[] converters) {
for (int i = 0; i < converters.length; i++) {
if (converters[i] instanceof Converter) {
xstream.registerConverter((Converter) converters[i], i);
}
else if (converters[i] instanceof SingleValueConverter) {
xstream.registerConverter((SingleValueConverter) converters[i], i);
}
else {
throw new IllegalArgumentException("Invalid ConverterMatcher [" + converters[i] + "]");
}
}
}
/**
* Set a alias/type map, consisting of string aliases mapped to Class
instances (or Strings to be
* converted to Class
instances).
*
* @see org.springframework.beans.propertyeditors.ClassEditor
*/
public void setAliases(Map aliases) {
for (Iterator iterator = aliases.entrySet().iterator(); iterator.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
// Check whether we need to convert from String to Class.
Class type = null;
if (entry.getValue() instanceof Class) {
type = (Class) entry.getValue();
}
else {
ClassEditor editor = new ClassEditor();
editor.setAsText(String.valueOf(entry.getValue()));
type = (Class) editor.getValue();
}
addAlias((String) entry.getKey(), type);
}
}
/**
* Adds an alias for the given type.
*
* @param name alias to be used for the type
* @param type the type to be aliased
*/
public void addAlias(String name, Class type) {
xstream.alias(name, type);
}
public boolean supports(Class clazz) {
return true;
}
/**
* Convert the given XStream exception to an appropriate exception from the org.springframework.oxm
* hierarchy.
*
* The default implementation delegates to XStreamUtils
. Can be overridden in subclasses.
*
* @param ex exception that occured
* @param marshalling indicates whether the exception occurs during marshalling (true
), or
* unmarshalling (false
)
* @return the corresponding XmlMappingException
instance
* @see XStreamUtils#convertXStreamException(Exception,boolean)
*/
public XmlMappingException convertXStreamException(Exception ex, boolean marshalling) {
return XStreamUtils.convertXStreamException(ex, marshalling);
}
/**
* Marshals the given graph to the given XStream HierarchicalStreamWriter. Converts exceptions using
* convertXStreamException
.
*/
private void marshal(Object graph, HierarchicalStreamWriter streamWriter) {
try {
xstream.marshal(graph, streamWriter);
}
catch (Exception ex) {
throw convertXStreamException(ex, true);
}
}
protected void marshalDomNode(Object graph, Node node) throws XmlMappingException {
HierarchicalStreamWriter streamWriter = null;
if (node instanceof Document) {
streamWriter = new DomWriter((Document) node);
}
else if (node instanceof Element) {
streamWriter = new DomWriter((Element) node);
}
else {
throw new IllegalArgumentException("DOMResult contains neither Document nor Element");
}
marshal(graph, streamWriter);
}
protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) throws XmlMappingException {
ContentHandler contentHandler = new StaxEventContentHandler(eventWriter);
marshalSaxHandlers(graph, contentHandler, null);
}
protected void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException {
try {
marshal(graph, new StaxWriter(new QNameMap(), streamWriter));
}
catch (XMLStreamException ex) {
throw convertXStreamException(ex, true);
}
}
protected void marshalOutputStream(Object graph, OutputStream outputStream)
throws XmlMappingException, IOException {
marshalWriter(graph, new OutputStreamWriter(outputStream, getEncoding()));
}
protected void marshalSaxHandlers(Object graph, ContentHandler contentHandler, LexicalHandler lexicalHandler)
throws XmlMappingException {
SaxWriter saxWriter = new SaxWriter();
saxWriter.setContentHandler(contentHandler);
marshal(graph, saxWriter);
}
protected void marshalWriter(Object graph, Writer writer) throws XmlMappingException, IOException {
marshal(graph, new CompactWriter(writer));
}
private Object unmarshal(HierarchicalStreamReader streamReader) {
try {
return xstream.unmarshal(streamReader);
}
catch (Exception ex) {
throw convertXStreamException(ex, false);
}
}
protected Object unmarshalDomNode(Node node) throws XmlMappingException {
HierarchicalStreamReader streamReader = null;
if (node instanceof Document) {
streamReader = new DomReader((Document) node);
}
else if (node instanceof Element) {
streamReader = new DomReader((Element) node);
}
else {
throw new IllegalArgumentException("DOMSource contains neither Document nor Element");
}
return unmarshal(streamReader);
}
protected Object unmarshalXmlEventReader(XMLEventReader eventReader) throws XmlMappingException {
try {
XMLStreamReader streamReader = new XmlEventStreamReader(eventReader);
return unmarshalXmlStreamReader(streamReader);
}
catch (XMLStreamException ex) {
throw convertXStreamException(ex, false);
}
}
protected Object unmarshalXmlStreamReader(XMLStreamReader streamReader) throws XmlMappingException {
return unmarshal(new StaxReader(new QNameMap(), streamReader));
}
protected Object unmarshalInputStream(InputStream inputStream) throws XmlMappingException, IOException {
return unmarshalReader(new InputStreamReader(inputStream, getEncoding()));
}
protected Object unmarshalReader(Reader reader) throws XmlMappingException, IOException {
return unmarshal(new XppReader(reader));
}
protected Object unmarshalSaxReader(XMLReader xmlReader, InputSource inputSource)
throws XmlMappingException, IOException {
throw new UnsupportedOperationException(
"XStreamMarshaller does not support unmarshalling using SAX XMLReaders");
}
}