手动实现XStream转换器工厂
XStream提供了与apache betwixt2类似的功能,缺点是在易用性差,优点是提供相当快的速度,即使个头巨大的XML也不在话下。
下面是一个通过XML配置装配XStream转换器工厂(方便获取XStream的工具),在大型的XML数据交换中发挥了出色的效果。本例是做复杂封装的一个测试。基本思想如此,放出来大家看看:
通常要转换的Java类是确定的。因此事先先定义一个比较复杂的Bean,然后以此为蓝本,看看转换器实现过程,以及测试效果。
一、目标Bean:
要转换的Java对象
public
class User {
private Long id;
private String username;
private String password;
private UserProfile userProfile = new UserProfile();
private Collection<Address> addresses = new HashSet<Address>();
private Long id;
private String username;
private String password;
private UserProfile userProfile = new UserProfile();
private Collection<Address> addresses = new HashSet<Address>();
public
class UserProfile {
private long id;
private String sex;
private Date birthday;
private String telphone;
private String email;
private Long userId;
private long id;
private String sex;
private Date birthday;
private String telphone;
private String email;
private Long userId;
public
class Address {
private Long id;
private String addr;
private String postCode;
private Long id;
private String addr;
private String postCode;
二、转换器配置
主要配置类和成员的别名信息
<?
xml
version
="1.0"
encoding
="GBK"
?>
<!--
Document : InfoUnits.xml.xml
Created on : 2008年6月9日, 下午8:10
Author : leizhimin
Description: 转换器配置文档所约定的配置规则如下:
1:根元素InfoUnits包含了若干要转换目标类的配置信息.
2:class属性为节点所对应的类,en为节点类的属性名,元素节点名为别名信息.
3:每个目标类都是配置文档根节点下的直接子节点,并且有class属性,而没有en属性.
4:目标类下面有还可以包含更多的子元素,如果子元素类型为自定义的Class类型.
-->
< InfoUnits >
< User class ="User" >
< ID en ="id" />
< MC en ="username" />
< MM en ="password" />
< UserProfile class ="UserProfile" en ="userProfile" >
< ID en ="id" />
< XB en ="sex" />
< SR en ="birthday" />
< DH en ="telphone" />
< YJDZ en ="email" />
< YHID en ="userId" />
</ UserProfile >
< Address-List en ="addersses" >
< Address class ="Address" >
< ID en ="id" />
< DZ en ="addr" />
< YB en ="postCode" />
</ Address >
</ Address-List >
</ User >
</ InfoUnits >
<!--
Document : InfoUnits.xml.xml
Created on : 2008年6月9日, 下午8:10
Author : leizhimin
Description: 转换器配置文档所约定的配置规则如下:
1:根元素InfoUnits包含了若干要转换目标类的配置信息.
2:class属性为节点所对应的类,en为节点类的属性名,元素节点名为别名信息.
3:每个目标类都是配置文档根节点下的直接子节点,并且有class属性,而没有en属性.
4:目标类下面有还可以包含更多的子元素,如果子元素类型为自定义的Class类型.
-->
< InfoUnits >
< User class ="User" >
< ID en ="id" />
< MC en ="username" />
< MM en ="password" />
< UserProfile class ="UserProfile" en ="userProfile" >
< ID en ="id" />
< XB en ="sex" />
< SR en ="birthday" />
< DH en ="telphone" />
< YJDZ en ="email" />
< YHID en ="userId" />
</ UserProfile >
< Address-List en ="addersses" >
< Address class ="Address" >
< ID en ="id" />
< DZ en ="addr" />
< YB en ="postCode" />
</ Address >
</ Address-List >
</ User >
</ InfoUnits >
三、转换器的构建
这是最复杂的部分,需要递归处理。
import com.thoughtworks.xstream.XStream;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* 转换器配置信息工具类,加载配置信息后会生成一个转换器工厂的注册表,为整个系统提供高效的转换器
*
* @author leizhimin
*/
public class InfoUnitConfigUtils {
//配置文档路径
public static final String CONFIG_FILE = "/InfoUnits.xml";
//XStream转换器注册表,也成为转换器工厂。
public static final Map<Class, XStream> converterMap = new HashMap<Class, XStream>();
static {
try {
reload();
} catch (ClassNotFoundException ex) {
System.out.println(ex.getMessage());
throw new RuntimeException(ex);
}
}
/**
* 重新载入配置文件信息并生成XStream转化器注册表
*
* @throws ClassNotFoundException
*/
public static void reload() throws ClassNotFoundException {
Document document = getCfgDocument();
//遍历配置文档,构建转换器工厂。
for (Iterator<Element> ite = document.getRootElement().elementIterator(); ite.hasNext();) {
Element element = ite.next();
XStream xStream = new XStream();
getAlias(element, xStream);
Class clazz = Class.forName(element.selectSingleNode( "@class").getText().trim());
converterMap.put(clazz, xStream);
}
}
/**
* 获取配置信息文档对象
*
* @return 配置信息文档对象
*/
public static Document getCfgDocument() {
InputStream ins = InfoUnitConfigUtils. class.getResourceAsStream(CONFIG_FILE);
SAXReader reader = new SAXReader();
Document doucument = null;
try {
doucument = reader.read(ins);
} catch (DocumentException ex) {
System.out.println(ex.getMessage());
throw new RuntimeException(ex);
}
return doucument;
}
public static Map<Class, XStream> getConverterMap() {
return converterMap;
}
/**
* 递归设置转换器的别名信息
*
* @param element 配置文档元素节点,该节点带有一个class的属性
* @param xStream 转换器对象
* @throws ClassNotFoundException
*/
public static void getAlias(Element element, XStream xStream) throws ClassNotFoundException {
//如果当前元素具有class属性,则为类型节点,设置类别名
if (element.selectSingleNode( "@class") != null) {
Class clazz = Class.forName(element.selectSingleNode( "@class").getText().trim());
//带有class属性的节点名作为类的别名
xStream.alias(element.getName().trim(), clazz);
}
//如果当前元素具有en属性,则为属性节点,设置属性的别名。
if (element.selectSingleNode( "@en") != null) {
//属性所属的类
Class fatherClazz = Class.forName(element.getParent().selectSingleNode( "@class").getText().trim());
xStream.aliasField(element.getName().trim(), fatherClazz, element.selectSingleNode( "@en").getText().trim());
}
//递归设置参数元素下所有元素的别名信息
List<Element> eList = element.elements();
if (eList.size() > 0) {
for (Element e : eList) {
getAlias(e, xStream); //递归设置
}
}
}
}
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* 转换器配置信息工具类,加载配置信息后会生成一个转换器工厂的注册表,为整个系统提供高效的转换器
*
* @author leizhimin
*/
public class InfoUnitConfigUtils {
//配置文档路径
public static final String CONFIG_FILE = "/InfoUnits.xml";
//XStream转换器注册表,也成为转换器工厂。
public static final Map<Class, XStream> converterMap = new HashMap<Class, XStream>();
static {
try {
reload();
} catch (ClassNotFoundException ex) {
System.out.println(ex.getMessage());
throw new RuntimeException(ex);
}
}
/**
* 重新载入配置文件信息并生成XStream转化器注册表
*
* @throws ClassNotFoundException
*/
public static void reload() throws ClassNotFoundException {
Document document = getCfgDocument();
//遍历配置文档,构建转换器工厂。
for (Iterator<Element> ite = document.getRootElement().elementIterator(); ite.hasNext();) {
Element element = ite.next();
XStream xStream = new XStream();
getAlias(element, xStream);
Class clazz = Class.forName(element.selectSingleNode( "@class").getText().trim());
converterMap.put(clazz, xStream);
}
}
/**
* 获取配置信息文档对象
*
* @return 配置信息文档对象
*/
public static Document getCfgDocument() {
InputStream ins = InfoUnitConfigUtils. class.getResourceAsStream(CONFIG_FILE);
SAXReader reader = new SAXReader();
Document doucument = null;
try {
doucument = reader.read(ins);
} catch (DocumentException ex) {
System.out.println(ex.getMessage());
throw new RuntimeException(ex);
}
return doucument;
}
public static Map<Class, XStream> getConverterMap() {
return converterMap;
}
/**
* 递归设置转换器的别名信息
*
* @param element 配置文档元素节点,该节点带有一个class的属性
* @param xStream 转换器对象
* @throws ClassNotFoundException
*/
public static void getAlias(Element element, XStream xStream) throws ClassNotFoundException {
//如果当前元素具有class属性,则为类型节点,设置类别名
if (element.selectSingleNode( "@class") != null) {
Class clazz = Class.forName(element.selectSingleNode( "@class").getText().trim());
//带有class属性的节点名作为类的别名
xStream.alias(element.getName().trim(), clazz);
}
//如果当前元素具有en属性,则为属性节点,设置属性的别名。
if (element.selectSingleNode( "@en") != null) {
//属性所属的类
Class fatherClazz = Class.forName(element.getParent().selectSingleNode( "@class").getText().trim());
xStream.aliasField(element.getName().trim(), fatherClazz, element.selectSingleNode( "@en").getText().trim());
}
//递归设置参数元素下所有元素的别名信息
List<Element> eList = element.elements();
if (eList.size() > 0) {
for (Element e : eList) {
getAlias(e, xStream); //递归设置
}
}
}
}
四、测试
有了转换器,就可以将射程范围(所配置的Java类)内的类以及格式匹配的XML做个双向的转换。
import com.thoughtworks.xstream.XStream;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashSet;
/**
* @author leizhimin
*/
public class SimpleConverter {
public static void main(String args[]) {
User user = new User(100L, "admin", "123456");
Calendar c = Calendar.getInstance();
c.set(1982, 3, 3);
UserProfile profile = new UserProfile(1000L, "M", c.getTime(), "66668888", "admin@yahoo.com.cn", 100L);
Address address1 = new Address(2000L, "郑州市花园路", "450001");
Address address2 = new Address(2001L, "郑州市经三路", "450001");
Collection<Address> co = new HashSet<Address>();
co.add(address1);
co.add(address2);
user.setAddresses(co);
user.setUserProfile(profile);
XStream xStream = InfoUnitConfigUtils.converterMap.get(User. class);
String xml = xStream.toXML(user);
System.out.println(xml);
User u = (User) xStream.fromXML(xml);
System.out.println(u);
String xml_address = " <Address>\n" +
" <id>2001</id>\n" +
" <addr>郑州市经三路</addr>\n" +
" <postCode>450001</postCode>\n" +
" </Address>";
Address add_1 = (Address) xStream.fromXML(xml_address);
System.out.println(add_1.getAddr());
XStream xStream1 = InfoUnitConfigUtils.getConverterMap().get(user.getClass());
System.out.println( "------");
System.out.println(xStream1.toXML(user));
NewClass nc = new NewClass(100L, address1);
nc.getUserProfiles().add(profile);
String nc_xml = xStream.toXML(nc);
System.out.println(nc_xml);
}
}
import java.util.Calendar;
import java.util.Collection;
import java.util.HashSet;
/**
* @author leizhimin
*/
public class SimpleConverter {
public static void main(String args[]) {
User user = new User(100L, "admin", "123456");
Calendar c = Calendar.getInstance();
c.set(1982, 3, 3);
UserProfile profile = new UserProfile(1000L, "M", c.getTime(), "66668888", "admin@yahoo.com.cn", 100L);
Address address1 = new Address(2000L, "郑州市花园路", "450001");
Address address2 = new Address(2001L, "郑州市经三路", "450001");
Collection<Address> co = new HashSet<Address>();
co.add(address1);
co.add(address2);
user.setAddresses(co);
user.setUserProfile(profile);
XStream xStream = InfoUnitConfigUtils.converterMap.get(User. class);
String xml = xStream.toXML(user);
System.out.println(xml);
User u = (User) xStream.fromXML(xml);
System.out.println(u);
String xml_address = " <Address>\n" +
" <id>2001</id>\n" +
" <addr>郑州市经三路</addr>\n" +
" <postCode>450001</postCode>\n" +
" </Address>";
Address add_1 = (Address) xStream.fromXML(xml_address);
System.out.println(add_1.getAddr());
XStream xStream1 = InfoUnitConfigUtils.getConverterMap().get(user.getClass());
System.out.println( "------");
System.out.println(xStream1.toXML(user));
NewClass nc = new NewClass(100L, address1);
nc.getUserProfiles().add(profile);
String nc_xml = xStream.toXML(nc);
System.out.println(nc_xml);
}
}
测试结果:
<User>
<ID>100</ID>
<MC>admin</MC>
<MM>123456</MM>
<UserProfile>
<ID>1000</ID>
<XB>M</XB>
<SR>1982-04-03 15:55:02.621 CST</SR>
<DH>66668888</DH>
<YJDZ> admin@yahoo.com.cn</YJDZ>
<YHID>100</YHID>
</UserProfile>
<addresses class= "set">
<Address>
<ID>2000</ID>
<DZ>郑州市花园路</DZ>
<YB>450001</YB>
</Address>
<Address>
<ID>2001</ID>
<DZ>郑州市经三路</DZ>
<YB>450001</YB>
</Address>
</addresses>
</User>
User@1bca5f1
郑州市经三路
------
<User>
<ID>100</ID>
<MC>admin</MC>
<MM>123456</MM>
<UserProfile>
<ID>1000</ID>
<XB>M</XB>
<SR>1982-04-03 15:55:02.621 CST</SR>
<DH>66668888</DH>
<YJDZ> admin@yahoo.com.cn</YJDZ>
<YHID>100</YHID>
</UserProfile>
<addresses class= "set">
<Address>
<ID>2000</ID>
<DZ>郑州市花园路</DZ>
<YB>450001</YB>
</Address>
<Address>
<ID>2001</ID>
<DZ>郑州市经三路</DZ>
<YB>450001</YB>
</Address>
</addresses>
</User>
<NewClass>
<id>100</id>
<adderss>
<ID>2000</ID>
<DZ>郑州市花园路</DZ>
<YB>450001</YB>
</adderss>
<userProfiles class= "set">
<UserProfile>
<ID>1000</ID>
<XB>M</XB>
<SR>1982-04-03 15:55:02.621 CST</SR>
<DH>66668888</DH>
<YJDZ> admin@yahoo.com.cn</YJDZ>
<YHID>100</YHID>
</UserProfile>
</userProfiles>
</NewClass>
Process finished with exit code 0
<ID>100</ID>
<MC>admin</MC>
<MM>123456</MM>
<UserProfile>
<ID>1000</ID>
<XB>M</XB>
<SR>1982-04-03 15:55:02.621 CST</SR>
<DH>66668888</DH>
<YJDZ> admin@yahoo.com.cn</YJDZ>
<YHID>100</YHID>
</UserProfile>
<addresses class= "set">
<Address>
<ID>2000</ID>
<DZ>郑州市花园路</DZ>
<YB>450001</YB>
</Address>
<Address>
<ID>2001</ID>
<DZ>郑州市经三路</DZ>
<YB>450001</YB>
</Address>
</addresses>
</User>
User@1bca5f1
郑州市经三路
------
<User>
<ID>100</ID>
<MC>admin</MC>
<MM>123456</MM>
<UserProfile>
<ID>1000</ID>
<XB>M</XB>
<SR>1982-04-03 15:55:02.621 CST</SR>
<DH>66668888</DH>
<YJDZ> admin@yahoo.com.cn</YJDZ>
<YHID>100</YHID>
</UserProfile>
<addresses class= "set">
<Address>
<ID>2000</ID>
<DZ>郑州市花园路</DZ>
<YB>450001</YB>
</Address>
<Address>
<ID>2001</ID>
<DZ>郑州市经三路</DZ>
<YB>450001</YB>
</Address>
</addresses>
</User>
<NewClass>
<id>100</id>
<adderss>
<ID>2000</ID>
<DZ>郑州市花园路</DZ>
<YB>450001</YB>
</adderss>
<userProfiles class= "set">
<UserProfile>
<ID>1000</ID>
<XB>M</XB>
<SR>1982-04-03 15:55:02.621 CST</SR>
<DH>66668888</DH>
<YJDZ> admin@yahoo.com.cn</YJDZ>
<YHID>100</YHID>
</UserProfile>
</userProfiles>
</NewClass>
Process finished with exit code 0
符合期望值,工厂构建成功!
从上面配置看,配置的代价是非常的小的,比起用Java一个一个设置更方便,更易于维护。甚至比betwixt2还简单很多。这个封装不是最简单的,仅仅是一个可行性的研究。