XStream對象相當Java對象和XML之間的轉換器,轉換過程是雙向的。創建XSteam對象的方式很簡單,只需要new XStream()即可。
Java到xml,用toXML()方法。
Xml到Java,用fromXML()方法。
在沒有任何設置默認情況下,java到xml的映射,是java成員名對應xml的元素名,java類的全名對應xml根元素的名字。而實際中,往往是xml和java類都有了,要完成相互轉換,必須進行別名映射。
別名配置包含三種情況:
1、類別名,用alias(String name, Class type)。
2、類成員別名,用aliasField(String alias, Class definedIn, String fieldName)
3、類成員作爲屬性別名,用 aliasAttribute(Class definedIn, String attributeName, String alias),單獨命名沒有意義,還要通過useAttributeFor(Class definedIn, String fieldName) 應用到某個類上。
別名的配置是非常重要的,但是其中有些細節問題很重要,在例子中會專門做詳細說明。
另外還有不太常用的方法:
addImplicitCollection(Class ownerType, String fieldName),去掉集合類型生成xml的父節點。
registerConverter(Converter converter) ,註冊一個轉換器。
如果你的xml很大,或者爲了安全性,以流的方式傳輸,那麼XStream也提供豐富的API,
如果這些基本的操作還不能滿足你應用的需求,XStream提供豐富的擴展點。你可以實現自己的轉換器。還可以利用XStream完成更負責的功能,比如輸出其他非xml格式的數據,還可以輸出html,還支持XML Dom類型數據,這些應用起來稍微複雜些。當然這些不是XStream應用的重點,也不用理會,真正需要的時候再查看API和源碼研究研究。
XStream的優點很多,但是也有一些小bug,比如在定義別名中的下劃線“_”轉換爲xml後會變成“__”這個符號,很變態。因此,儘量避免在別名中實用任何符號,卻是需要下劃線的時候,可以考慮實用連接符“-”,這個沒有問題。
另外,我們的Java Bean中,常常有一些常量,在轉換過程,XStream也會將這些常量轉換過去,形成常量的xml節點,這顯然不是想要的結果,對於常量字段,就不做轉換了。
下面給出一個非常典型的而且實用的例子,作爲對總結的補充:
-
import java.util.List;
-
-
public class Person {
-
private String name;
-
private String age;
-
private Profile profile;
-
private List<Address> addlist;
-
-
public Person(String name, String age, Profile profile, List<Address> addlist) {
-
this.name = name;
-
this.age = age;
-
this.profile = profile;
-
this.addlist = addlist;
-
}
-
-
public String toString() {
-
return "Person{" + "name='" + name + '\'' + ", age='" + age + '\'' + ", profile=" + profile + ", addlist=" + addlist + '}';
-
}
-
-
}
-
public class Profile {
-
private String job;
-
private String tel;
-
private String remark;
-
-
public Profile(String job, String tel, String remark) {
-
this.job = job;
-
this.tel = tel;
-
this.remark = remark;
-
}
-
-
public String toString() {
-
return "Profile{" + "job='" + job + '\'' + ", tel='" + tel + '\'' + ", remark='" + remark + '\'' + '}';
-
}
-
-
}
-
import java.util.List;
-
-
public class Person {
-
private String name;
-
private String age;
-
private Profile profile;
-
private List<Address> addlist;
-
-
public Person(String name, String age, Profile profile, List<Address> addlist) {
-
this.name = name;
-
this.age = age;
-
this.profile = profile;
-
this.addlist = addlist;
-
}
-
-
public String toString() {
-
return "Person{" + "name='" + name + '\'' + ", age='" + age + '\'' + ", profile=" + profile + ", addlist=" + addlist + '}';
-
}
-
-
}
-
import java.util.ArrayList;
-
import java.util.List;
-
-
import com.thoughtworks.xstream.XStream;
-
import com.thoughtworks.xstream.io.xml.DomDriver;
-
-
public class TestXStream {
-
-
public static void main(String args[]) {
-
Address address1 = new Address("鄭州市經三路", "450001");
-
Address address2 = new Address("北京市海淀區", "100000");
-
List<Address> addList = new ArrayList<Address>();
-
addList.add(address1);
-
addList.add(address2);
-
Profile profile = new Profile("軟件工程師", "13051594850", "備註說明");
-
Person person = new Person("X-rapido", "22", profile, addList);
-
-
-
XStream xStream = new XStream(new DomDriver());
-
-
-
xStream.alias("PERSON", Person.class);
-
xStream.alias("PROFILE", Profile.class);
-
xStream.alias("ADDRESS", Address.class);
-
-
String xml = xStream.toXML(person);
-
System.out.println("----------------第1次輸出, 設置類別名---------------- \n"+ xml + "\n");
-
-
-
-
xStream.aliasField("Name", Person.class, "name");
-
-
-
-
-
-
-
xStream.aliasField("PROFILE", Person.class, "profile");
-
xStream.aliasField("ADDLIST", Person.class, "addlist");
-
xStream.aliasField("Add", Address.class, "add");
-
xStream.aliasField("Job", Profile.class, "job");
-
-
String xml2 = xStream.toXML(person);
-
System.out.println("----------------第2次輸出, 設置類、字段別名---------------- \n"+ xml2 + "\n");
-
-
-
-
xStream.useAttributeFor(Address.class, "zipcode");
-
-
xStream.aliasAttribute(Address.class, "zipcode", "Zipcode");
-
-
String xml3 = xStream.toXML(person);
-
System.out.println("----------------第3次輸出, 設置類、字段別名,並在xml字段節點上增加屬性---------------- \n"+ xml3 + "\n");
-
-
-
-
String person_xml = "<PERSON>\n" +
-
" <Name>熔岩</Name>\n" +
-
" <age>27</age>\n" +
-
" <PROFILE>\n" +
-
" <Job>軟件工程師</Job>\n" +
-
" <tel>13512129933</tel>\n" +
-
" <remark>備註說明</remark>\n" +
-
" </PROFILE>\n" +
-
" <ADDLIST>\n" +
-
" <ADDRESS Zipcode=\"450001\">\n" +
-
" <Add>鄭州市經三路</Add>\n" +
-
" </ADDRESS>\n" +
-
" <ADDRESS Zipcode=\"710002\">\n" +
-
" <Add>西安市雁塔路</Add>\n" +
-
" </ADDRESS>\n" +
-
" </ADDLIST>\n" +
-
"</PERSON>";
-
String profile_xml = "<PROFILE>\n" +
-
" <Job>軟件工程師</Job>\n" +
-
" <tel>13512129933</tel>\n" +
-
" <remark>備註說明</remark>\n" +
-
" </PROFILE>";
-
String address_xml = "<ADDRESS Zipcode=\"710002\">\n" +
-
" <Add>西安市雁塔路</Add>\n" +
-
" </ADDRESS>";
-
-
-
System.out.println(xStream.fromXML(person_xml).toString());
-
System.out.println(xStream.fromXML(profile_xml).toString());
-
System.out.println(xStream.fromXML(address_xml).toString());
-
}
-
-
}
xStream.toXML()和xStream.fromXML()的常用方法
比如寫入文件方法
-
FileOutputStream fs = new FileOutputStream("d:/employeedata.txt");
-
xs.toXML(e, fs);
讀取
-
public static void main(String[] args) {
-
XStream xs = new XStream(new DomDriver());
-
Employee e = new Employee();
-
try {
-
FileInputStream fis = new FileInputStream("d:/employeedata.txt");
-
xs.fromXML(fis, e);
-
-
-
System.out.println(e.toString());
-
} catch (FileNotFoundException ex) {
-
ex.printStackTrace();
-
}
-
}
-
public static void main(String[] args) {
-
Employee e = new Employee();
-
e.setName("X-rapido");
-
e.setDesignation("Manager");
-
e.setDepartment("法國");
-
-
XStream xs = new XStream();
-
try {
-
FileOutputStream fs = new FileOutputStream("d:/employeedata.txt");
-
xs.toXML(e, fs);
-
-
System.out.println("XML寫入成功");
-
} catch (FileNotFoundException e1) {
-
e1.printStackTrace();
-
}
-
}
在實際中,類的屬性很多,嵌套層次也很複雜,如果僅僅使用XStream原生API來硬編碼設置別名等屬性,顯得太生硬也難以維護。完全可以考慮通過一個xml配置文件來定義所有用到的類的別名定義(包括其成員),然後,通過讀取配置構建一個XStream的工廠,在用到時候直接去取,而不是讓實用者組裝。我目前的一個項目中,就是這麼實現的,效果非常的好。
下面我給出針對上面提出的問題一個解決方案:
思想:考慮做一個過濾器,在xml轉java之前,在Java轉xml之後,應用這個過濾器。這個過濾器提供將xml中的“__”替換爲“-”,並且將xml中的不需要的節點剔除。
在過濾之前,我實現了個轉換器裝配,這一步通過xml來配置,並在java中獲取。
代碼就省略了,這一步很靈活,關鍵看你的應用了。
-
// 解決XStream對出現雙下劃線的bug
-
XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
爲了能過濾xml,我們需要用Dom4j遞歸遍歷xml文檔。下面一些算法代碼:
-
-
private static void recursiveElement(Element element) {
-
List<Element> elements = element.elements();
-
validXPathList.add(element.getPath());
-
if (elements.size() == 0) {
-
-
} else {
-
-
for (Iterator<Element> it = elements.iterator(); it.hasNext();) {
-
-
recursiveElement(it.next());
-
}
-
}
-
}
-
-
-
private static void recursiveFixElement(Element element) {
-
List<Element> elements = element.elements();
-
if (!validXPathList.contains(element.getPath())) {
-
element.addAttribute("delete", "true");
-
}
-
if (elements.size() == 0) {
-
-
} else {
-
-
for (Iterator<Element> it = elements.iterator(); it.hasNext();) {
-
Element e = it.next();
-
if (!validXPathList.contains(e.getPath())) {
-
e.addAttribute("delete", "true");
-
}
-
-
recursiveFixElement(e);
-
}
-
}
-
}
-
-
-
-
-
-
-
-
public static String filter(String xmlStr) {
-
Document document = null;
-
try {
-
document = DocumentHelper.parseText(xmlStr.replaceAll("__", "_"));
-
-
recursiveFixElement(document.getRootElement());
-
List<Node> nodeList = document.selectNodes("//@delete");
-
for (Node node : nodeList) {
-
node.getParent().detach();
-
}
-
} catch (DocumentException e) {
-
System.out.println(e.getMessage());
-
e.printStackTrace();
-
}
-
return document.asXML();
-
}
輸出內容如下
-
----------------第1次輸出, 設置類別名----------------
-
<PERSON>
-
<name>X-rapido</name>
-
<age>22</age>
-
<profile>
-
<job>軟件工程師</job>
-
<tel>13051594850</tel>
-
<remark>備註說明</remark>
-
</profile>
-
<addlist>
-
<ADDRESS>
-
<add>鄭州市經三路</add>
-
<zipcode>450001</zipcode>
-
</ADDRESS>
-
<ADDRESS>
-
<add>北京市海淀區</add>
-
<zipcode>100000</zipcode>
-
</ADDRESS>
-
</addlist>
-
</PERSON>
-
-
----------------第2次輸出, 設置類、字段別名----------------
-
<PERSON>
-
<Name>X-rapido</Name>
-
<age>22</age>
-
<PROFILE>
-
<Job>軟件工程師</Job>
-
<tel>13051594850</tel>
-
<remark>備註說明</remark>
-
</PROFILE>
-
<ADDLIST>
-
<ADDRESS>
-
<Add>鄭州市經三路</Add>
-
<zipcode>450001</zipcode>
-
</ADDRESS>
-
<ADDRESS>
-
<Add>北京市海淀區</Add>
-
<zipcode>100000</zipcode>
-
</ADDRESS>
-
</ADDLIST>
-
</PERSON>
-
-
----------------第3次輸出, 設置類、字段別名,並在xml字段節點上增加屬性----------------
-
<PERSON>
-
<Name>X-rapido</Name>
-
<age>22</age>
-
<PROFILE>
-
<Job>軟件工程師</Job>
-
<tel>13051594850</tel>
-
<remark>備註說明</remark>
-
</PROFILE>
-
<ADDLIST>
-
<ADDRESS Zipcode="450001">
-
<Add>鄭州市經三路</Add>
-
</ADDRESS>
-
<ADDRESS Zipcode="100000">
-
<Add>北京市海淀區</Add>
-
</ADDRESS>
-
</ADDLIST>
-
</PERSON>
-
-
Person{name='熔岩', age='27', profile=Profile{job='軟件工程師', tel='13512129933', remark='備註說明'}, addlist=[Address{add='鄭州市經三路', zipcode='450001'}, Address{add='西安市雁塔路', zipcode='710002'}]}
-
Profile{job='軟件工程師', tel='13512129933', remark='備註說明'}
-
Address{add='西安市雁塔路', zipcode='710002'}
轉換裝配方式有幾種,比如上面代碼中使用是DomDriver方式,也有StaxDriver方式,等默認XppDriver方式,但是xpp方式經常解析xml會出錯,比如將上面的代碼修改
-
-
-
XStream xStream = new XStream(new StaxDriver());
解析的輸出就變成以下結果
-
----------------第1次輸出, 設置類別名----------------
-
<?xml version="1.0" ?><PERSON><name>X-rapido</name><age>22</age><profile><job>軟件工程師</job><tel>13051594850</tel><remark>備註說明</remark></profile><addlist><ADDRESS><add>鄭州市經三路</add><zipcode>450001</zipcode></ADDRESS><ADDRESS><add>北京市海淀區</add><zipcode>100000</zipcode></ADDRESS></addlist></PERSON>
-
-
----------------第2次輸出, 設置類、字段別名----------------
-
<?xml version="1.0" ?><PERSON><Name>X-rapido</Name><age>22</age><PROFILE><Job>軟件工程師</Job><tel>13051594850</tel><remark>備註說明</remark></PROFILE><ADDLIST><ADDRESS><Add>鄭州市經三路</Add><zipcode>450001</zipcode></ADDRESS><ADDRESS><Add>北京市海淀區</Add><zipcode>100000</zipcode></ADDRESS></ADDLIST></PERSON>
-
-
----------------第3次輸出, 設置類、字段別名,並在xml字段節點上增加屬性----------------
-
<?xml version="1.0" ?><PERSON><Name>X-rapido</Name><age>22</age><PROFILE><Job>軟件工程師</Job><tel>13051594850</tel><remark>備註說明</remark></PROFILE><ADDLIST><ADDRESS Zipcode="450001"><Add>鄭州市經三路</Add></ADDRESS><ADDRESS Zipcode="100000"><Add>北京市海淀區</Add></ADDRESS></ADDLIST></PERSON>
-
-
Person{name='熔岩', age='27', profile=Profile{job='軟件工程師', tel='13512129933', remark='備註說明'}, addlist=[Address{add='鄭州市經三路', zipcode='450001'}, Address{add='西安市雁塔路', zipcode='710002'}]}
-
Profile{job='軟件工程師', tel='13512129933', remark='備註說明'}
-
Address{add='西安市雁塔路', zipcode='710002'}
Java轉換JSON
xStream對JSON也有非常好的支持,它提供了2個模型驅動。用這2個驅動可以完成Java對象到JSON的相互轉換。使用JettisonMappedXmlDriver驅動,將Java對象轉換成json,需要添加jettison.jar,JSON的轉換和XML的轉換用法一樣,只是創建XStream需要傳遞一個參數,這個參數就是xml到JSON映射轉換的驅動。這裏會講到兩個驅動,分別是JettisonMappedXmlDriver、JsonHierarchicalStreamDriver。
1、 用JettisonMappedXmlDriver轉換
-
public static void main(String args[]) {
-
Address address1 = new Address("鄭州市經三路", "450001");
-
Address address2 = new Address("北京市海淀區", "100000");
-
List<Address> addList = new ArrayList<Address>();
-
addList.add(address1);
-
addList.add(address2);
-
Profile profile = new Profile("軟件工程師", "13051594850", "備註說明");
-
Person person = new Person("X-rapido", "22", profile, addList);
-
-
XStream xStreamJ = new XStream(new JettisonMappedXmlDriver());
-
xStreamJ.setMode(XStream.NO_REFERENCES);
-
-
xStreamJ.alias("person", Person.class);
-
xStreamJ.alias("profile", Profile.class);
-
xStreamJ.alias("address", Address.class);
-
-
String xml = xStreamJ.toXML(person);
-
System.out.println("JSON:"+ xml);
-
}
-
{"person":{"name":"X-rapido","age":22,"profile":{"job":"軟件工程師","tel":13051594850,"remark":"備註說明"},"addlist":[{"address":[{"add":"鄭州市經三路","zipcode":450001},{"add":"北京市海淀區","zipcode":100000}]}]}}
2、 用JsonHierarchicalStreamDriver轉換
-
public static void main(String args[]) {
-
Address address1 = new Address("鄭州市經三路", "450001");
-
Address address2 = new Address("北京市海淀區", "100000");
-
List<Address> addList = new ArrayList<Address>();
-
addList.add(address1);
-
addList.add(address2);
-
Profile profile = new Profile("軟件工程師", "13051594850", "備註說明");
-
Person person = new Person("X-rapido", "22", profile, addList);
-
-
XStream xStreamJ = new XStream(new JsonHierarchicalStreamDriver());
-
-
xStreamJ.alias("person", Person.class);
-
xStreamJ.alias("profile", Profile.class);
-
xStreamJ.alias("address", Address.class);
-
-
String xml = xStreamJ.toXML(person);
-
System.out.println("JSON:"+ xml);
-
}
-
JSON:{"person": {
-
"name": "X-rapido",
-
"age": "22",
-
"profile": {
-
"job": "軟件工程師",
-
"tel": "13051594850",
-
"remark": "備註說明"
-
},
-
"addlist": [
-
{
-
"add": "鄭州市經三路",
-
"zipcode": "450001"
-
},
-
{
-
"add": "北京市海淀區",
-
"zipcode": "100000"
-
}
-
]
-
}}
使用JsonHierarchicalStreamDriver轉換默認會給轉換後的對象添加一個根節點,但是在構建JsonHierarchicalStreamDriver驅動的時候,你可以重寫createWriter方法,刪掉根節點。
看上面的結果,一個是默認帶根節點的JSON對象,它只是將類名作爲一個屬性,將對象作爲該屬性的一個值。而另一個沒有帶根屬性的JSON就是通過重寫createWriter方法完成的。
增加上面代碼,返回沒有根節點JSON
-
-
StreamJ = new XStream(new JsonHierarchicalStreamDriver() {
-
public HierarchicalStreamWriter createWriter(Writer out) {
-
return new JsonWriter(out, JsonWriter.DROP_ROOT_MODE);
-
}
-
);
-
JSON:{
-
"name": "X-rapido",
-
"age": "22",
-
"profile": {
-
"job": "軟件工程師",
-
"tel": "13051594850",
-
"remark": "備註說明"
-
},
-
"addlist": [
-
{
-
"add": "鄭州市經三路",
-
"zipcode": "450001"
-
},
-
{
-
"add": "北京市海淀區",
-
"zipcode": "100000"
-
}
-
]
-
}
將JSON轉換java對象
-
public static void main(String args[]) {
-
Address address1 = new Address("鄭州市經三路", "450001");
-
Address address2 = new Address("北京市海淀區", "100000");
-
List<Address> addList = new ArrayList<Address>();
-
addList.add(address1);
-
addList.add(address2);
-
Profile profile = new Profile("軟件工程師", "13051594850", "備註說明");
-
Person person = new Person("X-rapido", "22", profile, addList);
-
-
String json = "{\"person\":{\"name\":\"X-rapido\",\"age\":22,\"profile\":{\"job\":\"軟件工程師\",\"tel\":13051594850,\"remark\":\"備註說明\"},\"addlist\":[{\"address\":[{\"add\":\"鄭州市經三路\",\"zipcode\":450001},{\"add\":\"北京市海淀區\",\"zipcode\":100000}]}]}}";
-
XStream xStreamJ = new XStream(new JettisonMappedXmlDriver());
-
-
xStreamJ.alias("person", Person.class);
-
xStreamJ.alias("profile", Profile.class);
-
xStreamJ.alias("address", Address.class);
-
-
person = (Person) xStreamJ.fromXML(json);
-
System.out.println(person.toString());
-
}
-
Person{name='X-rapido', age='22', profile=Profile{job='軟件工程師', tel='13051594850', remark='備註說明'}, addlist=[Address{add='鄭州市經三路', zipcode='450001'}, Address{add='北京市海淀區', zipcode='100000'}]}
以上舉例使用的JavaBean對象,對於Map,List對象也是通用的,具有同樣功能的還有JsonLib包
XStream還提供了註解放方式,比如在字段上增加@XStreamOmitField表示忽略該字段,等等,這裏就不做講解了
示例代碼下載地址:http://download.csdn.net/detail/xiaokui_wingfly/8800295
參考文獻:http://www.jb51.net/article/14542.htm 、http://www.cnblogs.com/hoojo/archive/2011/04/22/2025197.html
轉載地址:http://blog.csdn.net/xiaokui_wingfly/article/details/46470145