Elim的博客

用来记录一些原创性的总结


Elim

JAXB系列之XmlJavaTypeAdapter

假设我们有下面这样一个User类。


	@XmlRootElement
	@XmlAccessorType(XmlAccessType.FIELD)
	public static class User {

		private Integer id;
		private String name;
		private Date createTime;
		//...忽略get/set方法
	}

我们使用如下这样的测试代码把它的一个对象转换为XML。


	@Test
	public void test() throws Exception {
		User user = new User();
		user.setId(1);
		user.setName("张三");
		user.setCreateTime(new Date());
		
		JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
		Marshaller marshaller = jaxbContext.createMarshaller();
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);//格式化XML
		marshaller.marshal(user, System.out);
	}

其生成的XML如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user>
    <id>1</id>
    <name>张三</name>
    <createTime>2017-06-29T19:40:19.821+08:00</createTime>
</user>

我们可以看到其中的日期类型转换后的结果通常不是我们希望的,我们可能更希望它以yyyy-MM-dd HH:mm:ss的方式进行转换。这个时候我们就可以使用XmlJavaTypeAdapter注解了,它允许我们指定一个XmlAdapter类的子类,这样对应的属性在转换为XML时会先通过指定的XmlAdapter进行转换,转换后的结果再进行XML转换。除了可以作为Java对象转换XML时的适配器外,它也可以作为XML转换Java的适配器。XmlAdapter中一共定义了两个抽象方法,marshalunmarshalmarshal用于Java对象转换XML,unmarshal用于XML转换Java对象。XmlAdapter上定义了两个泛型,第一个是Java转换XML时适配后返回的类型,第二个是原始的类型。所以我们如果需要把我们的java.util.Date类型的属性以yyyy-MM-dd HH:mm:ss格式进行输出,我们需要定义如下这样一个XmlAdapter类。


	public static class DateAdapter extends XmlAdapter<String, Date> {

		private static final String FORMAT = "yyyy-MM-dd HH:mm:ss";
		
		@Override
		public Date unmarshal(String v) throws Exception {
			if (v != null) {
				return new SimpleDateFormat(FORMAT).parse(v);
			}
			return null;
		}

		@Override
		public String marshal(Date v) throws Exception {
			if (v != null) {
				return new SimpleDateFormat(FORMAT).format(v);
			}
			return null;
		}
		
	}

需要注意的是在XmlAdapter中的marshalunmarshal方法中需要注意参数为null的情形。

然后我们就可以在UsercreateTime属性上加上@XmlJavaTypeAdapter(DateAdapter.class)。加上@XmlJavaTypeAdapter后的结果如下所示。


	@XmlRootElement
	@XmlAccessorType(XmlAccessType.FIELD)
	public static class User {

		private Integer id;
		private String name;
		@XmlJavaTypeAdapter(DateAdapter.class)
		private Date createTime;
		//...省略get/set方法
	}

然后我们使用如下代码进行测试:


	@Test
	public void test() throws Exception {

		User user = new User();
		user.setId(1);
		user.setName("张三");
		user.setCreateTime(new Date());
		
		JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
		Marshaller marshaller = jaxbContext.createMarshaller();
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);//格式化XML
		StringWriter writer = new StringWriter();
		marshaller.marshal(user, writer);
		String xml = writer.toString();
		
		User user2 = JAXB.unmarshal(new StringReader(xml), User.class);
		System.out.println(xml);
		System.out.println(user2.getCreateTime());
		
	}

在测试代码中我们先对User对象进行marshal操作,这个时候转换createTime时会调用DateAdapter类的marshal方法。接着我们通过生成的XML进行了一次unmarshal操作,这个时候会调用DateAdapterunmarshal方法。unmarshal后我们取了unmarshal生成的User对象的createTime并进行输出。完整输出结果如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user>
    <id>1</id>
    <name>张三</name>
    <createTime>2017-06-29 20:19:02</createTime>
</user>

Thu Jun 29 20:19:02 CST 2017

我们可以看到marshal后的日期格式是我们期望的yyyy-MM-dd HH:mm:ss,而unmarshal时基于yyyy-MM-dd HH:mm:ss也能正确的进行unmarshal,转换为对应的java.util.Date类型的对象。

(本文由Elim写于2017年6月29日)