项目简介
开源项目XStream是一套简单实用的类库,用于序列化对象与XML对象之间的相互转换。本文以XStream 1.1.2版进行说明,它具有以下几个特点:
Ø 灵活易用:在更高的层次上提供了简单、灵活、易用的统一接口,用户无需了解项目的底层细节
Ø 无需映射:大多数对象都可以在无需映射的情况下进行序列化与反序列化的操作
Ø 高速稳定:设计时力求达到的最重要的指标是解析速度快、占用内存少,以使之能够适用于大的对象处理或是对信息吞吐量要求高的系统
Ø 清晰易懂:项目采用reflection机制得到无冗余信息的XML文件。所生成的XML文件较本地Java序列化产物更简洁,格式更清晰,更便于用户阅读
Ø 无需修改:完全序列化包括private和final类型在内的全部内部字段。支持非公有类和内部类,类可以没有缺省的构造函数
Ø 易于集成:通过实现特定的接口,XStream可以直接与其它任何树型结构进行序列化与反序列化操作(而不仅仅是XML格式)
Ø 灵活转换:转换策略是可以定制的,允许用户自定义特殊类型的对象如何以XML格式存储。
Ø 错误处理:由于XML资料不合法而造成异常时,会提供详细地诊断信息帮助处理问题。
典型的应用场合
XStream主要应用于以下场合:
ü 数据对象的持久化
ü 数据交换
ü 配置文件
XStream系统架构分析
XStream的架构主要由四部分组成:
² Converters(转换器)
当XStream遇到需要轮换的对象时,它会委派给合适的转换器实现,XStream为通用类型提供了多种转换器实现,包括基本数据类型、String、Collections、Arrays、null、Date,等等。
XStream提供了缺省的转换器,当需要转换的数据对象没有匹配的转换器时会使用。是通过反射机制自动完成对对象内所有字段的映射。
² IO(输入/输出)
XStream是通过接口HierarchicalStramWriter和HierarchialStreamReader从底层XML数据中抽象而来的,上面的接口分别用于序列化和反序列化操作。
该特性使得XStream可以直接使用XML解析类从数据流中读取数据,或者是直接从已经存在的结构中提取数据(比如DOM)。如果XStream所操作的XML数据已经部分被其它XML解析类处理过了(比如SOAP类的实例),这样就可以避免在我们这一层的再次解析操作。
² Context(上下文引用)
在XStream序列化或反序列化对象时,它会创建两个类MarshallingContext和UnmarshallingContext,由它们来处理数据,以及委派合适的转换器。
XStream提供了三对上下文的缺省实现,它们之间有着细微的差别。缺省值可以通过方法XStream.setMode()来改变,需要传递下面参数中的一个:
Ø XStream.XPATH_REFERENCES
(缺省的)通过XPath引用来标识重复的引用。这样产生的XML具有最小的混乱性。
Ø XStream.ID_REFERENCES
使用ID引用来标识重复的引用。在一些场合,比如使用手写XML时,这样将会更易于操作
Ø XStream.NO_REFERENCES
这种情况将失去对图形对象的支持,仅把对象看作为树型结构。重复的引用被视作两个不同的对象,循环引用会导致异常产生。相对于上面两种模式,这种模式速度会更快,占用内存会更少。
² Facade(统一入口)
主要类XStream用作所有项目的入口点。它将上面所提及的重要组件集成在一起,提供更简单易用的API操作。
实例讲解
下面我们通过一个简单的例子来了解XStream是如何工作的。
创建需要序列化的对象类
/**个人信息类*/
public class Person
{
/**
* 构造函数
* @param fn 名称前部分
* @param ln名称后部分
* @param faxn 传真号码
* @param mobilen 移动电话
*/
public Person(String fn, String ln, PhoneNumber faxn, PhoneNumber mobilen)
{
this.firstName = fn;
this.lastName = ln;
this.faxNumber = faxn;
this.mobileNumber = mobilen;
}
/**
* 构造函数
* @param fn 名称前部分
* @param ln名称后部分
*/
public Person(String fn, String ln)
{
this.firstName = fn;
this.lastName = ln;
}
private String firstName;
private String lastName;
private PhoneNumber faxNumber;
private PhoneNumber mobileNumber;
/**
* @return Returns the faxNumber.
*/
public PhoneNumber getFaxNumber()
{
return faxNumber;
}
/**
* @param faxNumber
* The faxNumber to set.
*/
public void setFaxNumber(PhoneNumber faxNumber)
{
this.faxNumber = faxNumber;
}
/**
* @return Returns the firstName.
*/
public String getFirstName()
{
return firstName;
}
/**
* @param firstName
* The firstName to set.
*/
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
/**
* @return Returns the lastName.
*/
public String getLastName()
{
return lastName;
}
/**
* @param lastName
* The lastName to set.
*/
public void setLastName(String lastName)
{
this.lastName = lastName;
}
/**
* @return Returns the mobileNumber.
*/
public PhoneNumber getMobileNumber()
{
return mobileNumber;
}
/**
* @param mobileNumber
* The mobileNumber to set.
*/
public void setMobileNumber(PhoneNumber mobileNumber)
{
this.mobileNumber = mobileNumber;
}
}
/**电话号码信息类*/
public class PhoneNumber
{
private int phoneId;
private String phoneNumber;
/**
* 构造函数
* @param phoneId ID号码
* @param phoneNumber 电话号码
*/
public PhoneNumber(int phoneId, String phoneNumber)
{
super();
this.phoneId = phoneId;
this.phoneNumber = phoneNumber;
}
/**
* @return Returns the phoneNumber.
*/
public String getPhoneNumber()
{
return phoneNumber;
}
/**
* @param phoneNumber
* The phoneNumber to set.
*/
public void setPhoneNumber(String phoneNumber)
{
this.phoneNumber = phoneNumber;
}
/**
* @return Returns the phoneId.
*/
public int getPhoneId()
{
return phoneId;
}
/**
* @param phoneId
* The phoneId to set.
*/
public void setPhoneId(int phoneId)
{
this.phoneId = phoneId;
}
}
这里定义的属性都为private,XStream没有强制规定属性的可见性,默认情况下所有属性都会进行转换;虽然XStream不强制要求你必须要有setter和getter方法,也不要求你要有一个默认的类构造方法,但是在实际应用时,这些还是必须的,因为你在程序中需要为对象设置属性,需要构造对象,如果没有这些方法程序是不能编译通过的。
初始化XStream类
使用以下的语句进行初始化操作:
XStream xstream = new XStream();
默认情况下,XStream会采用Xpp3库,XPP3是一种运行效率非常高的XML全解析实现。如果你不想依靠Xpp3库的话,也可以使用一个标准的JAXP DOM解析器,可以采用以下语句进行初始化:
//不使用XPP3库
XStream xstream = new XStream(new DomDriver());
此xstream实例,为线程安全的,可以供多个线程进行调用,共享使用。参考com.thoughtworks.xstream.io.xml包,会发现系统提供了多种标识解析器供我们选择,包括,DomDriver、JDomDriver、StaxDriver等等。
注册需要转换的类的别名
现在,为了使XStream输出的XML文件更简练,我们要为需要转换的用户类设置别名,这些明会在转换过程中用于结点设置,注册别名使用以下的语句:
xstream.alias(“person”, Person.class);
xstream.alias(“phonenumber”, PhoneNumber.class);
当然,这一步不是必须的,如果不进行注册的话,XStream默认会在转换时,将用户类的全限定名称加入到XML文件中,如com.test.Person,如果在数据量较大的时候,生成的XML文件会增大不少。
将对象序列化为XML文档
到了这一步,我们就可以将一个Java对象序列化为XML文档了,先声明一个Person对象,然后为该对象设置一个手机号码,一个传真号码,使用下面的语句:
//生成Person对象,并注册属性
Person joe = new Person("Bill", "Gates");
joe.setFaxNumber(new PhoneNumber(101, "83501194"));
joe.setMobileNumber(new PhoneNumber(102, "13686447788"));
下面就是将生成的对象序列化,我们需要做的只是简单的使用下面的一个语句就可以:
String xmls = xstream.toXML(joe);
从结果我们可以看出,生成的XML文件非常简洁,除了必要的结点外,没有一丝多余的信息存在。参考下面的生成结果:
<person>
<firstName>Bill</firstName>
<lastName>Gates</lastName>
<faxNumber>
<phoneId>101</phoneId>
<phoneNumber>83501194</phoneNumber>
</faxNumber>
<mobileNumber>
<phoneId>102</phoneId>
<phoneNumber>13686447788</phoneNumber>
</mobileNumber>
</person>
从XML文件反序列化成对象
从一个XML文件反序列化出一个对象同样简单,一起来看下面的代码:
Person newJoe = (Person)xstream.fromXML(xmls);
结束语
通过上面的实例,我们可以看出,使用XStream来处理对象的序列化和反序列化很简单,只需要几行代码即可,而且该项目对标识的XML解析器有很好的支持,最重要的是生成的XML很“干净”,没有过多的冗余信息,该项目还在持续进行中,让我们一起来关注它吧!