SpringMVC XStream 返回Xml时完美支持List,Map输出

Easter79
• 阅读 673

此篇日志参考了
http://www.cnblogs.com/lucious/archive/2013/05/28/3104348.html

并在此源码上进行改动。

现支持,多种容器组合,无限循环嵌套,基本数据类型为null,则设置默认值,日期格式化。
改动源代码后,对于List的支持.每一个对象都是由data标签包裹。

SpringMVC XStream 返回Xml时完美支持List,Map输出

后台代码:

List<User> users = new ArrayList<User>();
        users.add(new User(1));
        users.add(new User(2));
        users.add(new User(3));

        model.addAttribute("list", users );

注意,在使用过程中,List不支持,基本类型的Xml转换,只支持对象Xml转换。

此方法是错误的正确的方法【要使用对象包装属性,并提供get set 方法,得到的结果如下图:

SpringMVC XStream 返回Xml时完美支持List,Map输出

List<String> list = new ArrayList<String>();
         list.add("l1");
         list.add("l2");
         list.add("l3");
         model.addAttribute("list", list);

改动源代码后,对于Map的完美支持。

SpringMVC XStream 返回Xml时完美支持List,Map输出

Map<String, Object> maps = new HashMap<String, Object>();
        maps.put("qqqq", "qqqqqq");
        maps.put("qqqq2", "qqqqqq2");
        maps.put("users", users);

        Map<String, Object> mm = new HashMap<String, Object>();
        mm.put("w1", "w1");
        mm.put("w2", "w2");
        mm.put("w3", "w3");

        Map<String, Object> mm2 = new HashMap<String, Object>();
        mm2.put("w1", "w1");
        mm2.put("w2", "w2");
        mm2.put("w3", "w3");

        mm.put("w4", mm2);
        maps.put("mm", mm);


        model.addAttribute("User1", maps);

改动源代码后,对于Java Ben的支持.Java Ben 中可放List,Map等容器,递归支持无限循环Xml节点组装。

SpringMVC XStream 返回Xml时完美支持List,Map输出

User u = new User();
        u.setUserID(userID);
        u.setUserName("测试一下");
        u.setBirth(new Date());

        List<User> users = new ArrayList<User>();
        users.add(new User(1));
        users.add(new User(2));
        users.add(new User(3));

        Map<String, Object> maps = new HashMap<String, Object>();
        maps.put("qqqq", "qqqqqq");
        maps.put("qqqq2", "qqqqqq2");
        maps.put("users", users);

        Map<String, Object> mm = new HashMap<String, Object>();
        mm.put("w1", "w1");
        mm.put("w2", "w2");
        mm.put("w3", "w3");

        Map<String, Object> mm2 = new HashMap<String, Object>();
        mm2.put("w1", "w1");
        mm2.put("w2", "w2");
        mm2.put("w3", "w3");

        mm.put("mm2", mm2);
        maps.put("mm", mm);

        u.setUsers(users);
        u.setMaps(maps);
        
        model.addAttribute("User", u);

User类源码:

package com.linapex.models;

import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * 项目名称:SpringMVC_build
 * 
 * 类名称:User
 * 
 * 创建人:LinApex
 * 
 * 创建时间:2013-9-4 下午3:05:28
 * 
 * 功能描述:
 */

@XmlRootElement
public class User
{

    private long userID;

    private String userName;

    private Date birth;

    private List<User> users;

    private Map<String, Object> maps;

    public Map<String, Object> getMaps()
    {
        return maps;
    }

    public void setMaps(Map<String, Object> maps)
    {
        this.maps = maps;
    }

    public User(long userID)
    {
        super();
        this.userID = userID;
    }

    public User()
    {
        super();
    }

    public List<User> getUsers()
    {
        return users;
    }

    public void setUsers(List<User> users)
    {
        this.users = users;
    }

    public String getUserName()
    {

        return userName;

    }

    public void setUserName(String userName)
    {

        this.userName = userName;

    }

    public Date getBirth()
    {

        return birth;

    }

    public void setBirth(Date birth)
    {

        this.birth = birth;

    }

    public long getUserID()
    {

        return userID;

    }

    public void setUserID(long userID)
    {

        this.userID = userID;

    }

    @Override
    public String toString()
    {
        return "User [userID=" + userID + ", userName=" + userName + ", birth=" + birth + ", users=" + users + "]";
    }

}

XStreamMarshaller类源码:

package com.linapex.web.expand;

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.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
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 org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.oxm.MarshallingFailureException;
import org.springframework.oxm.UncategorizedMappingException;
import org.springframework.oxm.UnmarshallingFailureException;
import org.springframework.oxm.XmlMappingException;
import org.springframework.oxm.support.AbstractMarshaller;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.StaxUtils;
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;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.ConverterMatcher;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.SingleValueConverter;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.StreamException;
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.XmlFriendlyReplacer;
import com.thoughtworks.xstream.io.xml.XppReader;
import com.thoughtworks.xstream.mapper.CannotResolveClassException;

/**
 * 项目名称:SpringMVC_build
 * 
 * 类名称:XStreamMarshaller
 * 
 * 创建人:LinApex
 * 
 * 创建时间:2013-9-4 下午5:25:18
 * 
 * 功能描述:
 */

public class XStreamMarshaller extends AbstractMarshaller implements InitializingBean, BeanClassLoaderAware
{

    /**
     * The default encoding used for stream access: UTF-8.
     */
    public static final String DEFAULT_ENCODING = "UTF-8";

    private final XStream xstream = new XStream();

    private HierarchicalStreamDriver streamDriver;

    private String encoding = DEFAULT_ENCODING;

    private Class[] supportedClasses;

    private ClassLoader classLoader;

    public XStreamMarshaller()
    {
        xstream.registerConverter(new DataTypeConverter());
    }

    /**
     * Returns the XStream instance used by this marshaller.
     */
    public XStream getXStream()
    {
        return this.xstream;
    }

    /**
     * Set the XStream mode.
     * 
     * @see XStream#XPATH_REFERENCES
     * @see XStream#ID_REFERENCES
     * @see XStream#NO_REFERENCES
     */
    public void setMode(int mode)
    {
        this.getXStream().setMode(mode);
    }

    /**
     * Set the <code>Converters</code> or <code>SingleValueConverters</code> to
     * be registered with the <code>XStream</code> instance.
     * 
     * @see Converter
     * @see SingleValueConverter
     */
    public void setConverters(ConverterMatcher[] converters)
    {
        for (int i = 0; i < converters.length; i++)
        {
            if (converters[i] instanceof Converter)
            {
                this.getXStream().registerConverter((Converter) converters[i], i);
            } else if (converters[i] instanceof SingleValueConverter)
            {
                this.getXStream().registerConverter((SingleValueConverter) converters[i], i);
            } else
            {
                throw new IllegalArgumentException("Invalid ConverterMatcher [" + converters[i] + "]");
            }
        }
    }

    /**
     * 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, ?> aliases) throws ClassNotFoundException
    {
        Map<String, Class<?>> classMap = toClassMap(aliases);

        for (Map.Entry<String, Class<?>> 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<String, ?> aliases) throws ClassNotFoundException
    {
        Map<String, Class<?>> classMap = toClassMap(aliases);

        for (Map.Entry<String, Class<?>> entry : classMap.entrySet())
        {
            this.getXStream().aliasType(entry.getKey(), entry.getValue());
        }
    }

    private Map<String, Class<?>> toClassMap(Map<String, ?> map) throws ClassNotFoundException
    {
        Map<String, Class<?>> result = new LinkedHashMap<String, Class<?>>(map.size());

        for (Map.Entry<String, ?> 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)
            {
                String s = (String) value;
                type = ClassUtils.forName(s, classLoader);
            } else
            {
                throw new IllegalArgumentException("Unknown value [" + value + "], expected String or Class");
            }
            result.put(key, type);
        }
        return result;
    }

    /**
     * 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 '.'");
            }
        }
    }

    /**
     * Set types to use XML attributes for.
     * 
     * @see XStream#useAttributeFor(Class)
     */
    public void setUseAttributeForTypes(Class[] types)
    {
        for (Class type : types)
        {
            this.getXStream().useAttributeFor(type);
        }
    }

    /**
     * Set the types to use XML attributes for. The given map can contain either
     * {@code <String, Class>} pairs, in which case
     * {@link XStream#useAttributeFor(String, Class)} is called. Alternatively,
     * the map can contain {@code <Class, String>} or
     * {@code <Class, List<String>>} 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)
            {
                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 <String, Class>," + " when using a map key of type String");
                }
            } 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 <Class, String> or <Class, List<String>> map," + " when using a map key of type Class");
                }
            } else
            {
                throw new IllegalArgumentException("Invalid argument 'attributes. " + "'useAttributesFor' property takes either a map key of type String or Class");
            }
        }
    }

    /**
     * Specify implicit collection fields, as a Map consisting of
     * <code>Class</code> instances mapped to comma separated collection field
     * names.
     * 
     * @see XStream#addImplicitCollection(Class, String)
     */
    public void setImplicitCollections(Map<Class<?>, String> implicitCollections)
    {
        for (Map.Entry<Class<?>, String> entry : implicitCollections.entrySet())
        {
            String[] collectionFields = StringUtils.commaDelimitedListToStringArray(entry.getValue());
            for (String collectionField : collectionFields)
            {
                this.getXStream().addImplicitCollection(entry.getKey(), collectionField);
            }
        }
    }

    /**
     * Specify omitted fields, as a Map consisting of <code>Class</code>
     * instances mapped to comma separated field names.
     * 
     * @see XStream#omitField(Class, String)
     */
    public void setOmittedFields(Map<Class<?>, String> omittedFields)
    {
        for (Map.Entry<Class<?>, String> entry : omittedFields.entrySet())
        {
            String[] fields = StringUtils.commaDelimitedListToStringArray(entry.getValue());
            for (String field : fields)
            {
                this.getXStream().omitField(entry.getKey(), field);
            }
        }
    }

    /**
     * Set the classes for which mappings will be read from class-level JDK 1.5+
     * annotation metadata.
     * 
     * @see XStream#processAnnotations(Class)
     */
    public void setAnnotatedClass(Class<?> annotatedClass)
    {
        Assert.notNull(annotatedClass, "'annotatedClass' must not be null");
        this.getXStream().processAnnotations(annotatedClass);
    }

    /**
     * Set annotated classes for which aliases will be read from class-level JDK
     * 1.5+ annotation metadata.
     * 
     * @see XStream#processAnnotations(Class[])
     */
    public void setAnnotatedClasses(Class<?>[] annotatedClasses)
    {
        Assert.notEmpty(annotatedClasses, "'annotatedClasses' must not be empty");
        this.getXStream().processAnnotations(annotatedClasses);
    }

    /**
     * Set the autodetection mode of XStream.
     * <p>
     * <strong>Note</strong> that auto-detection implies that the XStream is
     * configured while it is processing the XML streams, and thus introduces a
     * potential concurrency problem.
     * 
     * @see XStream#autodetectAnnotations(boolean)
     */
    public void setAutodetectAnnotations(boolean autodetectAnnotations)
    {
        this.getXStream().autodetectAnnotations(autodetectAnnotations);
    }

    /**
     * Set the XStream hierarchical stream driver to be used with stream readers
     * and writers.
     */
    public void setStreamDriver(HierarchicalStreamDriver streamDriver)
    {
        this.streamDriver = streamDriver;
    }

    /**
     * Set the encoding to be used for stream access.
     * 
     * @see #DEFAULT_ENCODING
     */
    public void setEncoding(String encoding)
    {
        this.encoding = encoding;
    }

    /**
     * Set the classes supported by this marshaller.
     * <p>
     * If this property is empty (the default), all classes are supported.
     * 
     * @see #supports(Class)
     */
    public void setSupportedClasses(Class[] supportedClasses)
    {
        this.supportedClasses = supportedClasses;
    }

    public void setBeanClassLoader(ClassLoader classLoader)
    {
        this.classLoader = classLoader;
    }

    public final void afterPropertiesSet() throws Exception
    {
        customizeXStream(getXStream());
    }

    /**
     * Template to allow for customizing of the given {@link XStream}.
     * <p>
     * The default implementation is empty.
     * 
     * @param xstream
     *            the {@code XStream} instance
     */
    protected void customizeXStream(XStream xstream)
    {
    }

    public boolean supports(Class clazz)
    {
        if (ObjectUtils.isEmpty(this.supportedClasses))
        {
            return true;
        } else
        {
            for (Class supportedClass : this.supportedClasses)
            {
                if (supportedClass.isAssignableFrom(clazz))
                {
                    return true;
                }
            }
            return false;
        }
    }

    // Marshalling

    @Override
    protected void marshalDomNode(Object graph, Node node) throws XmlMappingException
    {
        HierarchicalStreamWriter streamWriter;
        if (node instanceof Document)
        {
            streamWriter = new DomWriter((Document) node);
        } else if (node instanceof Element)
        {
            streamWriter = new DomWriter((Element) node, node.getOwnerDocument(), new XmlFriendlyReplacer());
        } else
        {
            throw new IllegalArgumentException("DOMResult contains neither Document nor Element");
        }
        marshal(graph, streamWriter);
    }

    @Override
    protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) throws XmlMappingException
    {
        ContentHandler contentHandler = StaxUtils.createContentHandler(eventWriter);
        marshalSaxHandlers(graph, contentHandler, null);
    }

    @Override
    protected void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException
    {
        try
        {
            marshal(graph, new StaxWriter(new QNameMap(), streamWriter));
        } catch (XMLStreamException ex)
        {
            throw convertXStreamException(ex, true);
        }
    }

    @Override
    protected void marshalOutputStream(Object graph, OutputStream outputStream) throws XmlMappingException, IOException
    {
        marshalWriter(graph, new OutputStreamWriter(outputStream, this.encoding));
    }

    @Override
    protected void marshalSaxHandlers(Object graph, ContentHandler contentHandler, LexicalHandler lexicalHandler) throws XmlMappingException
    {

        SaxWriter saxWriter = new SaxWriter();
        saxWriter.setContentHandler(contentHandler);
        marshal(graph, saxWriter);
    }

    @Override
    protected void marshalWriter(Object graph, Writer writer) throws XmlMappingException, IOException
    {
        if (this.streamDriver != null)
        {
            marshal(graph, this.streamDriver.createWriter(writer));
        } else
        {
            marshal(graph, new CompactWriter(writer));
        }
    }

    /**
     * Marshals the given graph to the given XStream HierarchicalStreamWriter.
     * Converts exceptions using {@link #convertXStreamException}.
     */
    private void marshal(Object graph, HierarchicalStreamWriter streamWriter)
    {
        try
        {
            // 转换别名,用类名作为别名
            if (graph instanceof List)
            {
                getXStream().marshal(graph, streamWriter);
            } else if (graph instanceof Map)
            {
                getXStream().marshal(graph, streamWriter);
            } else
            {
                xstream.alias(graph.getClass().getSimpleName(), graph.getClass());
                getXStream().marshal(graph, streamWriter);
            }
        } catch (Exception ex)
        {
            throw convertXStreamException(ex, true);
        } finally
        {
            try
            {
                streamWriter.flush();
            } catch (Exception ex)
            {
                logger.debug("Could not flush HierarchicalStreamWriter", ex);
            }
        }
    }

    // Unmarshalling

    @Override
    protected Object unmarshalDomNode(Node node) throws XmlMappingException
    {
        HierarchicalStreamReader streamReader;
        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);
    }

    @Override
    protected Object unmarshalXmlEventReader(XMLEventReader eventReader) throws XmlMappingException
    {
        try
        {
            XMLStreamReader streamReader = StaxUtils.createEventStreamReader(eventReader);
            return unmarshalXmlStreamReader(streamReader);
        } catch (XMLStreamException ex)
        {
            throw convertXStreamException(ex, false);
        }
    }

    @Override
    protected Object unmarshalXmlStreamReader(XMLStreamReader streamReader) throws XmlMappingException
    {
        return unmarshal(new StaxReader(new QNameMap(), streamReader));
    }

    @Override
    protected Object unmarshalInputStream(InputStream inputStream) throws XmlMappingException, IOException
    {
        return unmarshalReader(new InputStreamReader(inputStream, this.encoding));
    }

    @Override
    protected Object unmarshalReader(Reader reader) throws XmlMappingException, IOException
    {
        if (streamDriver != null)
        {
            return unmarshal(streamDriver.createReader(reader));
        } else
        {
            return unmarshal(new XppReader(reader));
        }
    }

    @Override
    protected Object unmarshalSaxReader(XMLReader xmlReader, InputSource inputSource) throws XmlMappingException, IOException
    {

        throw new UnsupportedOperationException("XStreamMarshaller does not support unmarshalling using SAX XMLReaders");
    }

    private Object unmarshal(HierarchicalStreamReader streamReader)
    {
        try
        {
            return this.getXStream().unmarshal(streamReader);
        } catch (Exception ex)
        {
            throw convertXStreamException(ex, false);
        }
    }

    /**
     * Convert the given XStream exception to an appropriate exception from the
     * <code>org.springframework.oxm</code> hierarchy.
     * <p>
     * A boolean flag is used to indicate whether this exception occurs during
     * marshalling or unmarshalling, since XStream itself does not make this
     * distinction in its exception hierarchy.
     * 
     * @param ex
     *            XStream exception that occured
     * @param marshalling
     *            indicates whether the exception occurs during marshalling (
     *            <code>true</code>), or unmarshalling (<code>false</code>)
     * @return the corresponding <code>XmlMappingException</code>
     */
    protected XmlMappingException convertXStreamException(Exception ex, boolean marshalling)
    {
        if (ex instanceof StreamException || ex instanceof CannotResolveClassException || ex instanceof ConversionException)
        {
            if (marshalling)
            {
                return new MarshallingFailureException("XStream marshalling exception", ex);
            } else
            {
                return new UnmarshallingFailureException("XStream unmarshalling exception", ex);
            }
        } else
        {
            // fallback
            return new UncategorizedMappingException("Unknown XStream exception", ex);
        }
    }
}

class DataTypeConverter implements Converter
{

    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context)
    {
        if (null == source)
        {
            return;
        }

        Class<?> cType = source.getClass();
        // 判断是否是List
        // 判断是否是Map
        // 判断是否是对象
        if (source instanceof List)
        {
            // 这里不需要自己指定list,因为在此构造函数中,已经设置了别名。
            // writer.startNode("list");
            for (Object o : (List<?>) source)
            {
                boolean isBaseType = isBaseType(o.getClass());
                if (isBaseType)
                {
                    writeData(o, o.getClass(), writer);
                } else
                {
                    writer.startNode("data");
                    // 递归组装xml.
                    marshal(o, writer, context);
                    writer.endNode();
                }
            }
            // writer.endNode();
        } else if (source instanceof Map)
        {
            // writer.startNode("map");
            for (Map.Entry<?, ?> entry : ((Map<?, ?>) source).entrySet())
            {
                writer.startNode(entry.getKey().toString());
                Object o = entry.getValue();
                boolean isBaseType = isBaseType(o.getClass());
                if (isBaseType)
                {
                    writeData(o, o.getClass(), writer);
                } else
                {
                    marshal(o, writer, context);
                }
                // writer.startNode("list");
                // marshal(o, writer, context);
                // writer.endNode();
                writer.endNode();
            } // 递归组装xml.
                // writer.endNode();
        } else
        {
            try
            {
                Field[] fields = cType.getDeclaredFields();
                for (Field field : fields)
                {
                    // 获得get方法
                    String temp1 = "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
                    Method m = null;
                    try
                    {
                        m = cType.getMethod(temp1, null);
                    } catch (Exception e)
                    {
                        continue;
                    }

                    String methodName = m.getName();
                    if (methodName.startsWith("get") && methodName != "getClass")
                    {
                        boolean isBaseType = isBaseType(m.getReturnType());
                        Object objGetValue = m.invoke(source, null);
                        if (isBaseType)
                        {
                            writer.startNode(field.getName());
                            writeData(objGetValue, m.getReturnType(), writer);
                            writer.endNode();
                        } else if (m.getReturnType().equals(List.class))
                        {
                            writer.startNode(field.getName());
                            if (objGetValue != null)
                            {
                                for (Object o : (List<?>) objGetValue)
                                {
                                    isBaseType = isBaseType(o.getClass());
                                    if (isBaseType)
                                    {
                                        writeData(o, o.getClass(), writer);
                                    } else
                                    {
                                        writer.startNode("data");
                                        // 递归组装xml.
                                        marshal(o, writer, context);
                                        writer.endNode();
                                    }
                                } // 递归组装xml.
                            }
                            writer.endNode();
                        } else if (m.getReturnType().equals(Map.class))
                        {
                            writer.startNode(field.getName());
                            if (objGetValue != null)
                            {
                                for (Map.Entry<?, ?> entry : ((Map<?, ?>) objGetValue).entrySet())
                                {
                                    Object o = entry.getValue();
                                    if (o == null)
                                    {
                                        continue;
                                    }
                                    isBaseType = isBaseType(o.getClass());
                                    if (isBaseType)
                                    {
                                        writer.startNode(entry.getKey().toString());
                                        writeData(o, o.getClass(), writer);
                                        writer.endNode();
                                    } else
                                    {
                                        writer.startNode(entry.getKey().toString());
                                        marshal(o, writer, context);
                                        writer.endNode();
                                    }
                                } // 递归组装xml.
                            }
                            writer.endNode();
                        }
                    }// end if
                }// end for
            } catch (Exception e)
            {
                e.printStackTrace();
            }// end catch
        }// end if
    }

    /**
     * 改写输出XML
     * 
     * @param o
     * @param ReturnType
     * @param writer
     */
    private void writeData(Object o, Class<?> ReturnType, HierarchicalStreamWriter writer)
    {
        // 如果是数字类型的话就要预设为0而不能为空
        // 如果是日期,则做转换yyyy-MM-dd HH:mm:ss.
        if (isNumValueType(ReturnType))
        {
            if (o == null)
            {
                writer.setValue("0");
            } else if (ReturnType.equals(Double.class) || ReturnType.equals(double.class) || ReturnType.equals(BigDecimal.class))
            {
                DecimalFormat df = new DecimalFormat("#.##");
                writer.setValue(df.format(o));
            } else
            {
                writer.setValue(o.toString());
            }
        } else if (ReturnType.equals(Date.class))
        {
            if (o == null)
            {
                writer.setValue("");
            } else
            {
                String result = "";
                try
                {
                    result = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(o);
                } catch (Exception e)
                {
                } finally
                {
                    writer.setValue(result);
                }
            }// end if (o == null)
        } else
        {
            writer.setValue(o == null ? "" : o.toString());
        }
    }

    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context)
    {
        return null;
    }

    public boolean canConvert(Class type)
    {
        return true;
    }

    /**
     * 判断是否为支持的数据类型
     * 
     * @param type
     * @return boolean
     */
    private boolean isBaseType(Class<?> type)
    {
        if (type.equals(Integer.class) || type.equals(Double.class) || type.equals(String.class) || type.equals(Boolean.class) || type.equals(Long.class) || type.equals(Short.class) || type.equals(Byte.class) || type.equals(Float.class) || type.equals(BigDecimal.class) || type.equals(int.class) || type.equals(float.class) || type.equals(long.class) || type.equals(double.class) || type.equals(short.class) || type.equals(boolean.class) || type.equals(byte.class) || type.equals(Date.class))
        {
            return true;
        }
        return false;
    }

    /**
     * 判断是否为数字类型
     * 
     * @param type
     * @return boolean
     */
    public boolean isNumValueType(Class<?> type)
    {
        if (type.equals(Integer.class) || type.equals(Double.class) || type.equals(Long.class) || type.equals(Short.class) || type.equals(Float.class) || type.equals(BigDecimal.class) || type.equals(int.class) || type.equals(float.class) || type.equals(long.class) || type.equals(double.class) || type.equals(short.class))
        {
            return true;
        }
        return false;
    }

}

其中 DataTypeConverter 比较重要,在XStreamMarshaller 类的构造函数中, xStream中注册了DataTypeConverter。

如果单独使用XStream,直接将DataTypeConverter类拿出即可,并注册.

xstream.registerConverter(new DataTypeConverter());

如果是在SprinMVC配置文件中,配置配置即可.

<!-- 根据客户端的不同的请求决定不同的view进行响应, 如 /blog/1.json /blog/1.xml -->
    <bean
        class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"
        p:order="1">
        <!-- 设置为true以忽略对Accept Header的支持 -->
        <property name="ignoreAcceptHeader" value="true" />
        <!-- 扩展名至mimeType的映射,即 /user.json => application/json -->
        <property name="favorPathExtension" value="true" />
        <!-- 在没有扩展名时即: "/user/1" 时的默认展现形式 -->
        <property name="defaultContentType" value="text/html" />
        <!-- 用于开启 /userinfo/123?format=json 的支持 -->
        <property name="favorParameter" value="false" />

        <!-- 扩展名至mimeType的映射,即 /user.json => application/json,需开启favorPathExtension为true的支持 -->
        <property name="mediaTypes">
            <map>
                <entry key="xml" value="application/xml" />
            </map>
        </property>
        <property name="defaultViews">
            <list>
                <!-- for application/xml -->
                <bean class="org.springframework.web.servlet.view.xml.MarshallingView">
                    <property name="marshaller">
                        <bean class="com.linapex.web.expand.XStreamMarshaller" />
                    </property>
                </bean>
            </list>
        </property>
    </bean>
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
PhoneGap设置Icon
参考:http://cordova.apache.org/docs/en/latest/config\_ref/images.html通过config.xml中的<icon标签来设置Icon<iconsrc"res/ios/icon.png"platform"ios"width"57"height"57"densi
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
Python time模块 返回格式化时间
常用命令  strftimetime.strftime("%Y%m%d%H:%M:%S",formattime)第二个参数为可选参数,不填第二个参数则返回格式化后的当前时间日期201812112:00:00time.strftime('%H:%M:%S')返回当前时间的时分秒time.strftim
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k