XStream、Jaxb是java中用于对象xml序列化/反序列化 的经典开源项目,利用它们将对象转换成xml时,经常会遇到日期(Date)、数字按指定格式输出的需求,下面是使用示例:
一、日期字段格式化输出
1.1 xStream
1 XStream x = new XStream(); 2 x.registerConverter(new DateConverter("yyyy-MM-dd HH:mm:ss", null,TimeZone.getTimeZone("GMT+8")));
xStream默认使用UTC时间格式输出,上面的代码演示了如何按北京时间输出 yyyy-MM-dd HH:mm:ss 格式
1.2 jaxb
jaxb处理这个要麻烦一点,先要创建一个Adapter,下面是示例
1 package com.cnblogs.yjmyzz.test; 2 3 import java.text.DateFormat; 4 import java.text.SimpleDateFormat; 5 import java.util.Date; 6 7 import javax.xml.bind.annotation.adapters.XmlAdapter; 8 9 public class JaxbDateAdapter extends XmlAdapter<String, Date> { 10 static final String STANDARM_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; 11 12 @Override 13 public Date unmarshal(String v) throws Exception { 14 if (v == null) { 15 return null; 16 } 17 18 DateFormat format = new SimpleDateFormat(STANDARM_DATE_FORMAT); 19 return format.parse(v); 20 } 21 22 @Override 23 public String marshal(Date v) throws Exception { 24 DateFormat format = new SimpleDateFormat(STANDARM_DATE_FORMAT); 25 return format.format(v); 26 } 27 }
然后要处理的dto类,相应的Date字段的get方法上使用刚才这个Adapter
@XmlJavaTypeAdapter(JaxbDateAdapter.class) public Date getCreateDate() { return createDate; }
注:不要在private上使用,最好将注解打在get方法上,否则有可能报错。
这里,再给一个List<T>类型的常见用法:
@XmlElementWrapper(name="details")
@XmlElement(name="detail")
public List<FSUDetail> getDetails() {
return details;
}
如果没有这二个注解,xml的结果类似:
<root>
...
<details>...</details>
<details>...</details>
...
</root>
加上这二个注释后,xml的结果类似:
<root>
...
<details>
<detail>...</detail>
<detail>...</detail>
</details>
...
</root>
二、数字格式化
假设我们要将一个Double型的成员,按中国货币的格式输出
2.1 xStream
默认的DoubleConverter满足不了要求,得从它派生一个子类来重写toString(Object obj)方法
1 package com.cnblogs.yjmyzz.test; 2 3 import java.text.NumberFormat; 4 import java.util.Locale; 5 6 import com.thoughtworks.xstream.converters.basic.DoubleConverter; 7 8 public class DoubleToCurrencyStringConverter extends DoubleConverter { 9 10 NumberFormat format; 11 12 public DoubleToCurrencyStringConverter(Locale local) { 13 format = NumberFormat.getCurrencyInstance(local); 14 } 15 16 public String toString(Object obj) { 17 return format.format(obj); 18 } 19 20 }
然后这样使用:
1 XStream x = new XStream(); 2 x.registerConverter(new DoubleToCurrencyStringConverter(Locale.CHINA));
2.2 Jaxb
仍然是按Adapter的老路,定义一个专用的Adapter
1 package com.cnblogs.yjmyzz.test; 2 3 import java.text.NumberFormat; 4 import java.util.Locale; 5 6 import javax.xml.bind.annotation.adapters.XmlAdapter; 7 8 public class JaxbNumberAdapter extends XmlAdapter<String, Number> { 9 10 @Override 11 public Number unmarshal(String v) throws Exception { 12 if (v == null) { 13 return null; 14 } 15 NumberFormat format = NumberFormat.getCurrencyInstance(Locale.CHINA); 16 return format.parse(v); 17 } 18 19 @Override 20 public String marshal(Number v) throws Exception { 21 NumberFormat format = NumberFormat.getCurrencyInstance(Locale.CHINA); 22 return format.format(v); 23 } 24 }
然后在相关的Double字段的get方法上,用注解使用这个Adapter
1 @XmlJavaTypeAdapter(JaxbNumberAdapter.class) 2 public Double getAmount() { 3 return amount; 4 }
最后附一个完整的示例:
为演示效果,先定义一个Dto类:
1 package com.cnblogs.yjmyzz.test; 2 3 import java.io.Serializable; 4 import java.util.Date; 5 6 import javax.xml.bind.annotation.XmlRootElement; 7 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 8 import com.thoughtworks.xstream.annotations.XStreamAlias; 9 10 @XmlRootElement(name = "sample") 11 @XStreamAlias("sample") 12 public class Sample implements Serializable{ 13 14 private static final long serialVersionUID = -6271703229325404123L; 15 16 private Double amount; 17 18 19 private Date createDate; 20 21 @XmlJavaTypeAdapter(JaxbNumberAdapter.class) 22 public Double getAmount() { 23 return amount; 24 } 25 26 public void setAmount(Double amount) { 27 this.amount = amount; 28 } 29 30 @XmlJavaTypeAdapter(JaxbDateAdapter.class) 31 public Date getCreateDate() { 32 return createDate; 33 } 34 35 public void setCreateDate(Date createDate) { 36 this.createDate = createDate; 37 } 38 39 }
同时为了使用jaxb更方便,定义一个JaxbUtil辅助类
1 package com.cnblogs.yjmyzz.util; 2 3 import java.io.StringReader; 4 import java.io.StringWriter; 5 6 import javax.xml.bind.JAXBContext; 7 import javax.xml.bind.Marshaller; 8 import javax.xml.bind.Unmarshaller; 9 10 public class JaxbUtil { 11 12 public static String toXml(Object obj) { 13 return toXml(obj, "UTF-8", false); 14 } 15 16 public static String toXml(Object obj, String encoding, 17 boolean isFormatOutput) { 18 String result = null; 19 try { 20 JAXBContext context = JAXBContext.newInstance(obj.getClass()); 21 Marshaller marshaller = context.createMarshaller(); 22 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, 23 isFormatOutput); 24 marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding); 25 26 StringWriter writer = new StringWriter(); 27 marshaller.marshal(obj, writer); 28 result = writer.toString(); 29 } catch (Exception e) { 30 e.printStackTrace(); 31 } 32 33 return result; 34 } 35 36 @SuppressWarnings("unchecked") 37 public static <T> T toObject(String xml, Class<T> c) { 38 T t = null; 39 try { 40 JAXBContext context = JAXBContext.newInstance(c); 41 Unmarshaller unmarshaller = context.createUnmarshaller(); 42 t = (T) unmarshaller.unmarshal(new StringReader(xml)); 43 } catch (Exception e) { 44 e.printStackTrace(); 45 } 46 47 return t; 48 } 49 }
完整的单元测试如下:
1 package com.cnblogs.yjmyzz.test; 2 3 import java.util.Calendar; 4 import java.util.Locale; 5 import java.util.TimeZone; 6 import org.junit.Test; 7 import com.cnblogs.yjmyzz.util.JaxbUtil; 8 import com.thoughtworks.xstream.converters.basic.DateConverter; 9 import com.thoughtworks.xstream.XStream; 10 11 public class XStreamAndJaxbTest { 12 13 private Sample getSampleObj() { 14 Calendar c = Calendar.getInstance(Locale.CHINA); 15 c.set(2014, 9, 31, 0, 0, 0); 16 Sample obj = new Sample(); 17 obj.setCreateDate(c.getTime()); 18 obj.setAmount(1234.5678); 19 return obj; 20 } 21 22 @Test 23 public void testXStream() { 24 XStream x = new XStream(); 25 x.alias("sample", Sample.class); 26 x.registerConverter(new DateConverter("yyyy-MM-dd HH:mm:ss", null, 27 TimeZone.getTimeZone("GMT+8"))); 28 x.registerConverter(new DoubleToCurrencyStringConverter(Locale.CHINA)); 29 System.out.println("==> xstream ==>\n"); 30 System.out.println(x.toXML(getSampleObj())); 31 System.out.println("\n\n"); 32 } 33 34 @Test 35 public void testJaxb() { 36 System.out.println("==> jaxb ==>\n"); 37 System.out.println(JaxbUtil.toXml(getSampleObj(),"UTF-8",true)); 38 System.out.println("\n\n"); 39 } 40 41 }
测试结果如下:
==> jaxb ==>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sample>
<amount>¥1,234.57</amount>
<createDate>2014-10-31 00:00:00</createDate>
</sample>
==> xstream ==>
<sample>
<amount>¥1,234.57</amount>
<createDate>2014-10-31 00:00:00</createDate>
</sample>