此篇日志参考了
http://www.cnblogs.com/lucious/archive/2013/05/28/3104348.html
并在此源码上进行改动。
现支持,多种容器组合,无限循环嵌套,基本数据类型为null,则设置默认值,日期格式化。
改动源代码后,对于List的支持.每一个对象都是由data标签包裹。

后台代码:
1List<User> users = new ArrayList<User>(); 2 users.add(new User(1)); 3 users.add(new User(2)); 4 users.add(new User(3)); 5 6 model.addAttribute("list", users );
注意,在使用过程中,List不支持,基本类型的Xml转换,只支持对象Xml转换。
此方法是错误的。正确的方法【要使用对象包装属性,并提供get set 方法】,得到的结果如下图:

1List<String> list = new ArrayList<String>(); 2 list.add("l1"); 3 list.add("l2"); 4 list.add("l3"); 5 model.addAttribute("list", list);
改动源代码后,对于Map的完美支持。

1Map<String, Object> maps = new HashMap<String, Object>(); 2 maps.put("qqqq", "qqqqqq"); 3 maps.put("qqqq2", "qqqqqq2"); 4 maps.put("users", users); 5 6 Map<String, Object> mm = new HashMap<String, Object>(); 7 mm.put("w1", "w1"); 8 mm.put("w2", "w2"); 9 mm.put("w3", "w3"); 10 11 Map<String, Object> mm2 = new HashMap<String, Object>(); 12 mm2.put("w1", "w1"); 13 mm2.put("w2", "w2"); 14 mm2.put("w3", "w3"); 15 16 mm.put("w4", mm2); 17 maps.put("mm", mm); 18 19 20 model.addAttribute("User1", maps);
改动源代码后,对于Java Ben的支持.Java Ben 中可放List,Map等容器,递归支持无限循环Xml节点组装。

1User u = new User(); 2 u.setUserID(userID); 3 u.setUserName("测试一下"); 4 u.setBirth(new Date()); 5 6 List<User> users = new ArrayList<User>(); 7 users.add(new User(1)); 8 users.add(new User(2)); 9 users.add(new User(3)); 10 11 Map<String, Object> maps = new HashMap<String, Object>(); 12 maps.put("qqqq", "qqqqqq"); 13 maps.put("qqqq2", "qqqqqq2"); 14 maps.put("users", users); 15 16 Map<String, Object> mm = new HashMap<String, Object>(); 17 mm.put("w1", "w1"); 18 mm.put("w2", "w2"); 19 mm.put("w3", "w3"); 20 21 Map<String, Object> mm2 = new HashMap<String, Object>(); 22 mm2.put("w1", "w1"); 23 mm2.put("w2", "w2"); 24 mm2.put("w3", "w3"); 25 26 mm.put("mm2", mm2); 27 maps.put("mm", mm); 28 29 u.setUsers(users); 30 u.setMaps(maps); 31 32 model.addAttribute("User", u);
User类源码:
1package com.linapex.models; 2 3import java.util.Date; 4import java.util.List; 5import java.util.Map; 6import javax.xml.bind.annotation.XmlRootElement; 7 8/** 9 * 项目名称:SpringMVC_build 10 * 11 * 类名称:User 12 * 13 * 创建人:LinApex 14 * 15 * 创建时间:2013-9-4 下午3:05:28 16 * 17 * 功能描述: 18 */ 19 20@XmlRootElement 21public class User 22{ 23 24 private long userID; 25 26 private String userName; 27 28 private Date birth; 29 30 private List<User> users; 31 32 private Map<String, Object> maps; 33 34 public Map<String, Object> getMaps() 35 { 36 return maps; 37 } 38 39 public void setMaps(Map<String, Object> maps) 40 { 41 this.maps = maps; 42 } 43 44 public User(long userID) 45 { 46 super(); 47 this.userID = userID; 48 } 49 50 public User() 51 { 52 super(); 53 } 54 55 public List<User> getUsers() 56 { 57 return users; 58 } 59 60 public void setUsers(List<User> users) 61 { 62 this.users = users; 63 } 64 65 public String getUserName() 66 { 67 68 return userName; 69 70 } 71 72 public void setUserName(String userName) 73 { 74 75 this.userName = userName; 76 77 } 78 79 public Date getBirth() 80 { 81 82 return birth; 83 84 } 85 86 public void setBirth(Date birth) 87 { 88 89 this.birth = birth; 90 91 } 92 93 public long getUserID() 94 { 95 96 return userID; 97 98 } 99 100 public void setUserID(long userID) 101 { 102 103 this.userID = userID; 104 105 } 106 107 @Override 108 public String toString() 109 { 110 return "User [userID=" + userID + ", userName=" + userName + ", birth=" + birth + ", users=" + users + "]"; 111 } 112 113}
XStreamMarshaller类源码:
1package com.linapex.web.expand; 2 3import java.io.IOException; 4import java.io.InputStream; 5import java.io.InputStreamReader; 6import java.io.OutputStream; 7import java.io.OutputStreamWriter; 8import java.io.Reader; 9import java.io.Writer; 10import java.lang.reflect.Field; 11import java.lang.reflect.Method; 12import java.math.BigDecimal; 13import java.text.DecimalFormat; 14import java.text.SimpleDateFormat; 15import java.util.Date; 16import java.util.LinkedHashMap; 17import java.util.List; 18import java.util.Map; 19import javax.xml.stream.XMLEventReader; 20import javax.xml.stream.XMLEventWriter; 21import javax.xml.stream.XMLStreamException; 22import javax.xml.stream.XMLStreamReader; 23import javax.xml.stream.XMLStreamWriter; 24import org.springframework.beans.factory.BeanClassLoaderAware; 25import org.springframework.beans.factory.InitializingBean; 26import org.springframework.oxm.MarshallingFailureException; 27import org.springframework.oxm.UncategorizedMappingException; 28import org.springframework.oxm.UnmarshallingFailureException; 29import org.springframework.oxm.XmlMappingException; 30import org.springframework.oxm.support.AbstractMarshaller; 31import org.springframework.util.Assert; 32import org.springframework.util.ClassUtils; 33import org.springframework.util.ObjectUtils; 34import org.springframework.util.StringUtils; 35import org.springframework.util.xml.StaxUtils; 36import org.w3c.dom.Document; 37import org.w3c.dom.Element; 38import org.w3c.dom.Node; 39import org.xml.sax.ContentHandler; 40import org.xml.sax.InputSource; 41import org.xml.sax.XMLReader; 42import org.xml.sax.ext.LexicalHandler; 43import com.thoughtworks.xstream.XStream; 44import com.thoughtworks.xstream.converters.ConversionException; 45import com.thoughtworks.xstream.converters.Converter; 46import com.thoughtworks.xstream.converters.ConverterMatcher; 47import com.thoughtworks.xstream.converters.MarshallingContext; 48import com.thoughtworks.xstream.converters.SingleValueConverter; 49import com.thoughtworks.xstream.converters.UnmarshallingContext; 50import com.thoughtworks.xstream.io.HierarchicalStreamDriver; 51import com.thoughtworks.xstream.io.HierarchicalStreamReader; 52import com.thoughtworks.xstream.io.HierarchicalStreamWriter; 53import com.thoughtworks.xstream.io.StreamException; 54import com.thoughtworks.xstream.io.xml.CompactWriter; 55import com.thoughtworks.xstream.io.xml.DomReader; 56import com.thoughtworks.xstream.io.xml.DomWriter; 57import com.thoughtworks.xstream.io.xml.QNameMap; 58import com.thoughtworks.xstream.io.xml.SaxWriter; 59import com.thoughtworks.xstream.io.xml.StaxReader; 60import com.thoughtworks.xstream.io.xml.StaxWriter; 61import com.thoughtworks.xstream.io.xml.XmlFriendlyReplacer; 62import com.thoughtworks.xstream.io.xml.XppReader; 63import com.thoughtworks.xstream.mapper.CannotResolveClassException; 64 65/** 66 * 项目名称:SpringMVC_build 67 * 68 * 类名称:XStreamMarshaller 69 * 70 * 创建人:LinApex 71 * 72 * 创建时间:2013-9-4 下午5:25:18 73 * 74 * 功能描述: 75 */ 76 77public class XStreamMarshaller extends AbstractMarshaller implements InitializingBean, BeanClassLoaderAware 78{ 79 80 /** 81 * The default encoding used for stream access: UTF-8. 82 */ 83 public static final String DEFAULT_ENCODING = "UTF-8"; 84 85 private final XStream xstream = new XStream(); 86 87 private HierarchicalStreamDriver streamDriver; 88 89 private String encoding = DEFAULT_ENCODING; 90 91 private Class[] supportedClasses; 92 93 private ClassLoader classLoader; 94 95 public XStreamMarshaller() 96 { 97 xstream.registerConverter(new DataTypeConverter()); 98 } 99 100 /** 101 * Returns the XStream instance used by this marshaller. 102 */ 103 public XStream getXStream() 104 { 105 return this.xstream; 106 } 107 108 /** 109 * Set the XStream mode. 110 * 111 * @see XStream#XPATH_REFERENCES 112 * @see XStream#ID_REFERENCES 113 * @see XStream#NO_REFERENCES 114 */ 115 public void setMode(int mode) 116 { 117 this.getXStream().setMode(mode); 118 } 119 120 /** 121 * Set the <code>Converters</code> or <code>SingleValueConverters</code> to 122 * be registered with the <code>XStream</code> instance. 123 * 124 * @see Converter 125 * @see SingleValueConverter 126 */ 127 public void setConverters(ConverterMatcher[] converters) 128 { 129 for (int i = 0; i < converters.length; i++) 130 { 131 if (converters[i] instanceof Converter) 132 { 133 this.getXStream().registerConverter((Converter) converters[i], i); 134 } else if (converters[i] instanceof SingleValueConverter) 135 { 136 this.getXStream().registerConverter((SingleValueConverter) converters[i], i); 137 } else 138 { 139 throw new IllegalArgumentException("Invalid ConverterMatcher [" + converters[i] + "]"); 140 } 141 } 142 } 143 144 /** 145 * Sets an alias/type map, consisting of string aliases mapped to classes. 146 * Keys are aliases; values are either {@code Class} instances, or String 147 * class names. 148 * 149 * @see XStream#alias(String, Class) 150 */ 151 public void setAliases(Map<String, ?> aliases) throws ClassNotFoundException 152 { 153 Map<String, Class<?>> classMap = toClassMap(aliases); 154 155 for (Map.Entry<String, Class<?>> entry : classMap.entrySet()) 156 { 157 this.getXStream().alias(entry.getKey(), entry.getValue()); 158 } 159 } 160 161 /** 162 * Sets the aliases by type map, consisting of string aliases mapped to 163 * classes. Any class that is assignable to this type will be aliased to the 164 * same name. Keys are aliases; values are either {@code Class} instances, 165 * or String class names. 166 * 167 * @see XStream#aliasType(String, Class) 168 */ 169 public void setAliasesByType(Map<String, ?> aliases) throws ClassNotFoundException 170 { 171 Map<String, Class<?>> classMap = toClassMap(aliases); 172 173 for (Map.Entry<String, Class<?>> entry : classMap.entrySet()) 174 { 175 this.getXStream().aliasType(entry.getKey(), entry.getValue()); 176 } 177 } 178 179 private Map<String, Class<?>> toClassMap(Map<String, ?> map) throws ClassNotFoundException 180 { 181 Map<String, Class<?>> result = new LinkedHashMap<String, Class<?>>(map.size()); 182 183 for (Map.Entry<String, ?> entry : map.entrySet()) 184 { 185 String key = entry.getKey(); 186 Object value = entry.getValue(); 187 Class type; 188 if (value instanceof Class) 189 { 190 type = (Class) value; 191 } else if (value instanceof String) 192 { 193 String s = (String) value; 194 type = ClassUtils.forName(s, classLoader); 195 } else 196 { 197 throw new IllegalArgumentException("Unknown value [" + value + "], expected String or Class"); 198 } 199 result.put(key, type); 200 } 201 return result; 202 } 203 204 /** 205 * Sets a field alias/type map, consiting of field names 206 * 207 * @param aliases 208 * @throws ClassNotFoundException 209 * @throws NoSuchFieldException 210 * @see XStream#aliasField(String, Class, String) 211 */ 212 public void setFieldAliases(Map<String, String> aliases) throws ClassNotFoundException, NoSuchFieldException 213 { 214 for (Map.Entry<String, String> entry : aliases.entrySet()) 215 { 216 String alias = entry.getValue(); 217 String field = entry.getKey(); 218 int idx = field.lastIndexOf('.'); 219 if (idx != -1) 220 { 221 String className = field.substring(0, idx); 222 Class clazz = ClassUtils.forName(className, classLoader); 223 String fieldName = field.substring(idx + 1); 224 this.getXStream().aliasField(alias, clazz, fieldName); 225 } else 226 { 227 throw new IllegalArgumentException("Field name [" + field + "] does not contain '.'"); 228 } 229 } 230 } 231 232 /** 233 * Set types to use XML attributes for. 234 * 235 * @see XStream#useAttributeFor(Class) 236 */ 237 public void setUseAttributeForTypes(Class[] types) 238 { 239 for (Class type : types) 240 { 241 this.getXStream().useAttributeFor(type); 242 } 243 } 244 245 /** 246 * Set the types to use XML attributes for. The given map can contain either 247 * {@code <String, Class>} pairs, in which case 248 * {@link XStream#useAttributeFor(String, Class)} is called. Alternatively, 249 * the map can contain {@code <Class, String>} or 250 * {@code <Class, List<String>>} pairs, which results in 251 * {@link XStream#useAttributeFor(Class, String)} calls. 252 */ 253 public void setUseAttributeFor(Map<?, ?> attributes) 254 { 255 for (Map.Entry<?, ?> entry : attributes.entrySet()) 256 { 257 if (entry.getKey() instanceof String) 258 { 259 if (entry.getValue() instanceof Class) 260 { 261 this.getXStream().useAttributeFor((String) entry.getKey(), (Class) entry.getValue()); 262 } else 263 { 264 throw new IllegalArgumentException("Invalid argument 'attributes'. 'useAttributesFor' property takes map of <String, Class>," + " when using a map key of type String"); 265 } 266 } else if (entry.getKey() instanceof Class) 267 { 268 Class<?> key = (Class<?>) entry.getKey(); 269 if (entry.getValue() instanceof String) 270 { 271 this.getXStream().useAttributeFor(key, (String) entry.getValue()); 272 } else if (entry.getValue() instanceof List) 273 { 274 List list = (List) entry.getValue(); 275 276 for (Object o : list) 277 { 278 if (o instanceof String) 279 { 280 this.getXStream().useAttributeFor(key, (String) o); 281 } 282 } 283 } else 284 { 285 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"); 286 } 287 } else 288 { 289 throw new IllegalArgumentException("Invalid argument 'attributes. " + "'useAttributesFor' property takes either a map key of type String or Class"); 290 } 291 } 292 } 293 294 /** 295 * Specify implicit collection fields, as a Map consisting of 296 * <code>Class</code> instances mapped to comma separated collection field 297 * names. 298 * 299 * @see XStream#addImplicitCollection(Class, String) 300 */ 301 public void setImplicitCollections(Map<Class<?>, String> implicitCollections) 302 { 303 for (Map.Entry<Class<?>, String> entry : implicitCollections.entrySet()) 304 { 305 String[] collectionFields = StringUtils.commaDelimitedListToStringArray(entry.getValue()); 306 for (String collectionField : collectionFields) 307 { 308 this.getXStream().addImplicitCollection(entry.getKey(), collectionField); 309 } 310 } 311 } 312 313 /** 314 * Specify omitted fields, as a Map consisting of <code>Class</code> 315 * instances mapped to comma separated field names. 316 * 317 * @see XStream#omitField(Class, String) 318 */ 319 public void setOmittedFields(Map<Class<?>, String> omittedFields) 320 { 321 for (Map.Entry<Class<?>, String> entry : omittedFields.entrySet()) 322 { 323 String[] fields = StringUtils.commaDelimitedListToStringArray(entry.getValue()); 324 for (String field : fields) 325 { 326 this.getXStream().omitField(entry.getKey(), field); 327 } 328 } 329 } 330 331 /** 332 * Set the classes for which mappings will be read from class-level JDK 1.5+ 333 * annotation metadata. 334 * 335 * @see XStream#processAnnotations(Class) 336 */ 337 public void setAnnotatedClass(Class<?> annotatedClass) 338 { 339 Assert.notNull(annotatedClass, "'annotatedClass' must not be null"); 340 this.getXStream().processAnnotations(annotatedClass); 341 } 342 343 /** 344 * Set annotated classes for which aliases will be read from class-level JDK 345 * 1.5+ annotation metadata. 346 * 347 * @see XStream#processAnnotations(Class[]) 348 */ 349 public void setAnnotatedClasses(Class<?>[] annotatedClasses) 350 { 351 Assert.notEmpty(annotatedClasses, "'annotatedClasses' must not be empty"); 352 this.getXStream().processAnnotations(annotatedClasses); 353 } 354 355 /** 356 * Set the autodetection mode of XStream. 357 * <p> 358 * <strong>Note</strong> that auto-detection implies that the XStream is 359 * configured while it is processing the XML streams, and thus introduces a 360 * potential concurrency problem. 361 * 362 * @see XStream#autodetectAnnotations(boolean) 363 */ 364 public void setAutodetectAnnotations(boolean autodetectAnnotations) 365 { 366 this.getXStream().autodetectAnnotations(autodetectAnnotations); 367 } 368 369 /** 370 * Set the XStream hierarchical stream driver to be used with stream readers 371 * and writers. 372 */ 373 public void setStreamDriver(HierarchicalStreamDriver streamDriver) 374 { 375 this.streamDriver = streamDriver; 376 } 377 378 /** 379 * Set the encoding to be used for stream access. 380 * 381 * @see #DEFAULT_ENCODING 382 */ 383 public void setEncoding(String encoding) 384 { 385 this.encoding = encoding; 386 } 387 388 /** 389 * Set the classes supported by this marshaller. 390 * <p> 391 * If this property is empty (the default), all classes are supported. 392 * 393 * @see #supports(Class) 394 */ 395 public void setSupportedClasses(Class[] supportedClasses) 396 { 397 this.supportedClasses = supportedClasses; 398 } 399 400 public void setBeanClassLoader(ClassLoader classLoader) 401 { 402 this.classLoader = classLoader; 403 } 404 405 public final void afterPropertiesSet() throws Exception 406 { 407 customizeXStream(getXStream()); 408 } 409 410 /** 411 * Template to allow for customizing of the given {@link XStream}. 412 * <p> 413 * The default implementation is empty. 414 * 415 * @param xstream 416 * the {@code XStream} instance 417 */ 418 protected void customizeXStream(XStream xstream) 419 { 420 } 421 422 public boolean supports(Class clazz) 423 { 424 if (ObjectUtils.isEmpty(this.supportedClasses)) 425 { 426 return true; 427 } else 428 { 429 for (Class supportedClass : this.supportedClasses) 430 { 431 if (supportedClass.isAssignableFrom(clazz)) 432 { 433 return true; 434 } 435 } 436 return false; 437 } 438 } 439 440 // Marshalling 441 442 @Override 443 protected void marshalDomNode(Object graph, Node node) throws XmlMappingException 444 { 445 HierarchicalStreamWriter streamWriter; 446 if (node instanceof Document) 447 { 448 streamWriter = new DomWriter((Document) node); 449 } else if (node instanceof Element) 450 { 451 streamWriter = new DomWriter((Element) node, node.getOwnerDocument(), new XmlFriendlyReplacer()); 452 } else 453 { 454 throw new IllegalArgumentException("DOMResult contains neither Document nor Element"); 455 } 456 marshal(graph, streamWriter); 457 } 458 459 @Override 460 protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) throws XmlMappingException 461 { 462 ContentHandler contentHandler = StaxUtils.createContentHandler(eventWriter); 463 marshalSaxHandlers(graph, contentHandler, null); 464 } 465 466 @Override 467 protected void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException 468 { 469 try 470 { 471 marshal(graph, new StaxWriter(new QNameMap(), streamWriter)); 472 } catch (XMLStreamException ex) 473 { 474 throw convertXStreamException(ex, true); 475 } 476 } 477 478 @Override 479 protected void marshalOutputStream(Object graph, OutputStream outputStream) throws XmlMappingException, IOException 480 { 481 marshalWriter(graph, new OutputStreamWriter(outputStream, this.encoding)); 482 } 483 484 @Override 485 protected void marshalSaxHandlers(Object graph, ContentHandler contentHandler, LexicalHandler lexicalHandler) throws XmlMappingException 486 { 487 488 SaxWriter saxWriter = new SaxWriter(); 489 saxWriter.setContentHandler(contentHandler); 490 marshal(graph, saxWriter); 491 } 492 493 @Override 494 protected void marshalWriter(Object graph, Writer writer) throws XmlMappingException, IOException 495 { 496 if (this.streamDriver != null) 497 { 498 marshal(graph, this.streamDriver.createWriter(writer)); 499 } else 500 { 501 marshal(graph, new CompactWriter(writer)); 502 } 503 } 504 505 /** 506 * Marshals the given graph to the given XStream HierarchicalStreamWriter. 507 * Converts exceptions using {@link #convertXStreamException}. 508 */ 509 private void marshal(Object graph, HierarchicalStreamWriter streamWriter) 510 { 511 try 512 { 513 // 转换别名,用类名作为别名 514 if (graph instanceof List) 515 { 516 getXStream().marshal(graph, streamWriter); 517 } else if (graph instanceof Map) 518 { 519 getXStream().marshal(graph, streamWriter); 520 } else 521 { 522 xstream.alias(graph.getClass().getSimpleName(), graph.getClass()); 523 getXStream().marshal(graph, streamWriter); 524 } 525 } catch (Exception ex) 526 { 527 throw convertXStreamException(ex, true); 528 } finally 529 { 530 try 531 { 532 streamWriter.flush(); 533 } catch (Exception ex) 534 { 535 logger.debug("Could not flush HierarchicalStreamWriter", ex); 536 } 537 } 538 } 539 540 // Unmarshalling 541 542 @Override 543 protected Object unmarshalDomNode(Node node) throws XmlMappingException 544 { 545 HierarchicalStreamReader streamReader; 546 if (node instanceof Document) 547 { 548 streamReader = new DomReader((Document) node); 549 } else if (node instanceof Element) 550 { 551 streamReader = new DomReader((Element) node); 552 } else 553 { 554 throw new IllegalArgumentException("DOMSource contains neither Document nor Element"); 555 } 556 return unmarshal(streamReader); 557 } 558 559 @Override 560 protected Object unmarshalXmlEventReader(XMLEventReader eventReader) throws XmlMappingException 561 { 562 try 563 { 564 XMLStreamReader streamReader = StaxUtils.createEventStreamReader(eventReader); 565 return unmarshalXmlStreamReader(streamReader); 566 } catch (XMLStreamException ex) 567 { 568 throw convertXStreamException(ex, false); 569 } 570 } 571 572 @Override 573 protected Object unmarshalXmlStreamReader(XMLStreamReader streamReader) throws XmlMappingException 574 { 575 return unmarshal(new StaxReader(new QNameMap(), streamReader)); 576 } 577 578 @Override 579 protected Object unmarshalInputStream(InputStream inputStream) throws XmlMappingException, IOException 580 { 581 return unmarshalReader(new InputStreamReader(inputStream, this.encoding)); 582 } 583 584 @Override 585 protected Object unmarshalReader(Reader reader) throws XmlMappingException, IOException 586 { 587 if (streamDriver != null) 588 { 589 return unmarshal(streamDriver.createReader(reader)); 590 } else 591 { 592 return unmarshal(new XppReader(reader)); 593 } 594 } 595 596 @Override 597 protected Object unmarshalSaxReader(XMLReader xmlReader, InputSource inputSource) throws XmlMappingException, IOException 598 { 599 600 throw new UnsupportedOperationException("XStreamMarshaller does not support unmarshalling using SAX XMLReaders"); 601 } 602 603 private Object unmarshal(HierarchicalStreamReader streamReader) 604 { 605 try 606 { 607 return this.getXStream().unmarshal(streamReader); 608 } catch (Exception ex) 609 { 610 throw convertXStreamException(ex, false); 611 } 612 } 613 614 /** 615 * Convert the given XStream exception to an appropriate exception from the 616 * <code>org.springframework.oxm</code> hierarchy. 617 * <p> 618 * A boolean flag is used to indicate whether this exception occurs during 619 * marshalling or unmarshalling, since XStream itself does not make this 620 * distinction in its exception hierarchy. 621 * 622 * @param ex 623 * XStream exception that occured 624 * @param marshalling 625 * indicates whether the exception occurs during marshalling ( 626 * <code>true</code>), or unmarshalling (<code>false</code>) 627 * @return the corresponding <code>XmlMappingException</code> 628 */ 629 protected XmlMappingException convertXStreamException(Exception ex, boolean marshalling) 630 { 631 if (ex instanceof StreamException || ex instanceof CannotResolveClassException || ex instanceof ConversionException) 632 { 633 if (marshalling) 634 { 635 return new MarshallingFailureException("XStream marshalling exception", ex); 636 } else 637 { 638 return new UnmarshallingFailureException("XStream unmarshalling exception", ex); 639 } 640 } else 641 { 642 // fallback 643 return new UncategorizedMappingException("Unknown XStream exception", ex); 644 } 645 } 646} 647 648class DataTypeConverter implements Converter 649{ 650 651 public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) 652 { 653 if (null == source) 654 { 655 return; 656 } 657 658 Class<?> cType = source.getClass(); 659 // 判断是否是List 660 // 判断是否是Map 661 // 判断是否是对象 662 if (source instanceof List) 663 { 664 // 这里不需要自己指定list,因为在此构造函数中,已经设置了别名。 665 // writer.startNode("list"); 666 for (Object o : (List<?>) source) 667 { 668 boolean isBaseType = isBaseType(o.getClass()); 669 if (isBaseType) 670 { 671 writeData(o, o.getClass(), writer); 672 } else 673 { 674 writer.startNode("data"); 675 // 递归组装xml. 676 marshal(o, writer, context); 677 writer.endNode(); 678 } 679 } 680 // writer.endNode(); 681 } else if (source instanceof Map) 682 { 683 // writer.startNode("map"); 684 for (Map.Entry<?, ?> entry : ((Map<?, ?>) source).entrySet()) 685 { 686 writer.startNode(entry.getKey().toString()); 687 Object o = entry.getValue(); 688 boolean isBaseType = isBaseType(o.getClass()); 689 if (isBaseType) 690 { 691 writeData(o, o.getClass(), writer); 692 } else 693 { 694 marshal(o, writer, context); 695 } 696 // writer.startNode("list"); 697 // marshal(o, writer, context); 698 // writer.endNode(); 699 writer.endNode(); 700 } // 递归组装xml. 701 // writer.endNode(); 702 } else 703 { 704 try 705 { 706 Field[] fields = cType.getDeclaredFields(); 707 for (Field field : fields) 708 { 709 // 获得get方法 710 String temp1 = "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1); 711 Method m = null; 712 try 713 { 714 m = cType.getMethod(temp1, null); 715 } catch (Exception e) 716 { 717 continue; 718 } 719 720 String methodName = m.getName(); 721 if (methodName.startsWith("get") && methodName != "getClass") 722 { 723 boolean isBaseType = isBaseType(m.getReturnType()); 724 Object objGetValue = m.invoke(source, null); 725 if (isBaseType) 726 { 727 writer.startNode(field.getName()); 728 writeData(objGetValue, m.getReturnType(), writer); 729 writer.endNode(); 730 } else if (m.getReturnType().equals(List.class)) 731 { 732 writer.startNode(field.getName()); 733 if (objGetValue != null) 734 { 735 for (Object o : (List<?>) objGetValue) 736 { 737 isBaseType = isBaseType(o.getClass()); 738 if (isBaseType) 739 { 740 writeData(o, o.getClass(), writer); 741 } else 742 { 743 writer.startNode("data"); 744 // 递归组装xml. 745 marshal(o, writer, context); 746 writer.endNode(); 747 } 748 } // 递归组装xml. 749 } 750 writer.endNode(); 751 } else if (m.getReturnType().equals(Map.class)) 752 { 753 writer.startNode(field.getName()); 754 if (objGetValue != null) 755 { 756 for (Map.Entry<?, ?> entry : ((Map<?, ?>) objGetValue).entrySet()) 757 { 758 Object o = entry.getValue(); 759 if (o == null) 760 { 761 continue; 762 } 763 isBaseType = isBaseType(o.getClass()); 764 if (isBaseType) 765 { 766 writer.startNode(entry.getKey().toString()); 767 writeData(o, o.getClass(), writer); 768 writer.endNode(); 769 } else 770 { 771 writer.startNode(entry.getKey().toString()); 772 marshal(o, writer, context); 773 writer.endNode(); 774 } 775 } // 递归组装xml. 776 } 777 writer.endNode(); 778 } 779 }// end if 780 }// end for 781 } catch (Exception e) 782 { 783 e.printStackTrace(); 784 }// end catch 785 }// end if 786 } 787 788 /** 789 * 改写输出XML 790 * 791 * @param o 792 * @param ReturnType 793 * @param writer 794 */ 795 private void writeData(Object o, Class<?> ReturnType, HierarchicalStreamWriter writer) 796 { 797 // 如果是数字类型的话就要预设为0而不能为空 798 // 如果是日期,则做转换yyyy-MM-dd HH:mm:ss. 799 if (isNumValueType(ReturnType)) 800 { 801 if (o == null) 802 { 803 writer.setValue("0"); 804 } else if (ReturnType.equals(Double.class) || ReturnType.equals(double.class) || ReturnType.equals(BigDecimal.class)) 805 { 806 DecimalFormat df = new DecimalFormat("#.##"); 807 writer.setValue(df.format(o)); 808 } else 809 { 810 writer.setValue(o.toString()); 811 } 812 } else if (ReturnType.equals(Date.class)) 813 { 814 if (o == null) 815 { 816 writer.setValue(""); 817 } else 818 { 819 String result = ""; 820 try 821 { 822 result = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(o); 823 } catch (Exception e) 824 { 825 } finally 826 { 827 writer.setValue(result); 828 } 829 }// end if (o == null) 830 } else 831 { 832 writer.setValue(o == null ? "" : o.toString()); 833 } 834 } 835 836 public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) 837 { 838 return null; 839 } 840 841 public boolean canConvert(Class type) 842 { 843 return true; 844 } 845 846 /** 847 * 判断是否为支持的数据类型 848 * 849 * @param type 850 * @return boolean 851 */ 852 private boolean isBaseType(Class<?> type) 853 { 854 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)) 855 { 856 return true; 857 } 858 return false; 859 } 860 861 /** 862 * 判断是否为数字类型 863 * 864 * @param type 865 * @return boolean 866 */ 867 public boolean isNumValueType(Class<?> type) 868 { 869 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)) 870 { 871 return true; 872 } 873 return false; 874 } 875 876}
其中 DataTypeConverter 比较重要,在XStreamMarshaller 类的构造函数中, xStream中注册了DataTypeConverter。
如果单独使用XStream,直接将DataTypeConverter类拿出即可,并注册.
xstream.registerConverter(new DataTypeConverter());
如果是在SprinMVC配置文件中,配置配置即可.
1<!-- 根据客户端的不同的请求决定不同的view进行响应, 如 /blog/1.json /blog/1.xml --> 2 <bean 3 class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver" 4 p:order="1"> 5 <!-- 设置为true以忽略对Accept Header的支持 --> 6 <property name="ignoreAcceptHeader" value="true" /> 7 <!-- 扩展名至mimeType的映射,即 /user.json => application/json --> 8 <property name="favorPathExtension" value="true" /> 9 <!-- 在没有扩展名时即: "/user/1" 时的默认展现形式 --> 10 <property name="defaultContentType" value="text/html" /> 11 <!-- 用于开启 /userinfo/123?format=json 的支持 --> 12 <property name="favorParameter" value="false" /> 13 14 <!-- 扩展名至mimeType的映射,即 /user.json => application/json,需开启favorPathExtension为true的支持 --> 15 <property name="mediaTypes"> 16 <map> 17 <entry key="xml" value="application/xml" /> 18 </map> 19 </property> 20 <property name="defaultViews"> 21 <list> 22 <!-- for application/xml --> 23 <bean class="org.springframework.web.servlet.view.xml.MarshallingView"> 24 <property name="marshaller"> 25 <bean class="com.linapex.web.expand.XStreamMarshaller" /> 26 </property> 27 </bean> 28 </list> 29 </property> 30 </bean>