此篇日志参考了
http://www.cnblogs.com/lucious/archive/2013/05/28/3104348.html
并在此源码上进行改动。
现支持,多种容器组合,无限循环嵌套,基本数据类型为null,则设置默认值,日期格式化。
改动源代码后,对于List的支持.每一个对象都是由data标签包裹。
后台代码:
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 方法】,得到的结果如下图:
List<String> list = new ArrayList<String>();
list.add("l1");
list.add("l2");
list.add("l3");
model.addAttribute("list", 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节点组装。
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>