公司給工具作個接口測試,工具返回給咱們文件格式爲xml,咱們平臺採用JAVA開發,爲了從此的數據持久化 和 查看結果的方便,應該將XML轉換爲Object,這樣接觸到了XStream這個類庫,雖然小,但功能着實強大。html
網上有一篇灰常詳細的文章對XStream進行介紹http://www.cnblogs.com/hoojo/archive/2011/04/22/2025197.html,感謝博主。java
另外這裏對其進行一些補充,主要是在解析XML中遇到過一些不太符合常規的XML格式,這樣就須要一些不一樣常規的處理方式來解決:web
1、Converter接口的使用json
話說有這麼個響應文件須要進行解析微信
" <Result value=\"Success\"> <EngineName>webxxx</EngineName> <CPU>50</CPU> <MEM>30</MEM> <DISK>40</DISK> <Funcs> <Func name=\"木馬掃描\">OK</Func> <Func name=\"存儲\">NO</Func> </Funcs> </Result> ";
根據XML對應格式,能夠將其解析成一個Result對象,其中包含app
engineName,cPU,mEM,dISK,List<Func>屬性,其中Func對象對應<Func/>結點。按照普通的方法,沒法將其中<Func/>結點的text值賦值給Func對象。dom
這時Converter就派上用場了:首先定義好各種ide
public class GetStateResult { private String value; private String engineName; private String cPU; private String mEM; private String dISK; private List<com.time.dbapp.vo.GetStateFunc> funcs; ... } public class GetStateFuc{ private String value;//對應 OK/NO private String name; .... } //Converter實現類 class RequestCoverter implements Converter{ public void marshal(Object arg0, HierarchicalStreamWriter writer,MarshallingContext context) {} public Object unmarshal(HierarchicalStreamReader reader,UnmarshallingContext context) { com.time.dbapp.vo.GetStateFunc result = new com.time.dbapp.vo.GetStateFunc(); result.setName(reader.getAttribute("name"));//獲取順序有關,必須先獲取name,再獲取value,不然報 錯 result.setValue(reader.getValue()); return result; } public boolean canConvert(Class clazz) { return clazz.equals(com.time.dbapp.vo.GetStateFunc.class); } }
大概做用就是當解析到設置好的映射結點時(canConvert返回true表示),執行unmarshal方法去解析對應的結點工具
接下來進行XStream的配置: 測試
XStream xstream = new XStream(); xstream.registerConverter(new RequestCoverter()); //註冊轉換器 xstream.alias("result", GetStateResult.class); xstream.alias("func", com.time.dbapp.vo.GetStateFunc.class); xstream.useAttributeFor(com.time.dbapp.vo.GetStateFunc.class, "name");
這樣就能夠進行解析了!
2、XppDriver 、PrettyPrintWriter、XppReader類
我理解爲一種解析輸入輸出驅動,該類繼承AbstractXppDriver,實現了HierarchicalStreamDriver接口,(能夠查看API文檔)先看類中方法,就大概知道幹嗎的了:
XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { } public HierarchicalStreamReader createReader(Reader reader){}
大概猜到,咱們能夠在解析(Reader) 和 輸出(Writer)進行一些操做了。這個在上面時候有這方面須要呢。問題是這麼來的,測試工具返回給咱們的文檔結構中(如上)全部結點名稱都爲大寫,可是按照JavaBean的命名規範,全部java屬性應該以小寫字母。當按照XStream處理原理,他們在實例化對象,並賦值的過程是沒有經過GET/SET方法來的,直接操做屬性,這樣就會存在問題,一個大寫一個小寫是不能完成映射的。爲了讓代碼既符合規範,又能正確解析XML文件。這樣XppDriver閃亮登場。
經過API中XStream(HierarchicalStreamDriver hierarchicalStreamDriver)看到實例化XStream對象能夠傳遞HierarchicalStreamDriver對象來處理。
因此我簡單封裝一下XStream
class RequestXStream { RequestXStream(){ super(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { public void startNode(String name){//將對應的屬性名 轉化成首字母大寫 super.startNode(StringUtil.upperFirstChar(name)); } }; } public HierarchicalStreamReader createReader(Reader reader){ return new XppReader(reader){ public String getNodeName(){ return StringUtil.lowerFirstChar(super.getNodeName());{//將對應的屬性名 轉化成首字母小寫 } }; } }); } }
這樣就能夠很好的處理結點首字母大小寫的問題了,其中StringUtil.upperFirstChar爲本身寫的簡單處理方法,將字符串首字母大寫。固然針對不一樣的問題,能夠實現XPPDriver具體不一樣方法,這些能夠查看API,進行解決,總之XStream是個不錯的類庫,提供了豐富的接口,知足不一樣的文件格式解析和轉換。
代碼示例:
工具類: MessageUtil.java
package com.example.gongzhong1.utils; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.core.util.QuickWriter; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; import com.thoughtworks.xstream.io.xml.XppDriver; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import java.io.Writer; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Pattern; public class MessageUtil { public static final String RESP_MESSAGE_TYPE_TEXT = "text"; /** * 解析微信請求的xml格式的數據,返回以參數名爲key,參數值爲vlaue的map * @param request * @return * @throws Exception */ public static Map<String,String> parseXml(HttpServletRequest request) throws Exception{ //將解析結果放入到該map中 Map<String,String> requestMap = new HashMap<>(); //從request中取得輸入流 ServletInputStream inputStream = request.getInputStream(); //讀取輸入流 SAXReader reader = new SAXReader(); Document doucument = reader.read(inputStream); //獲取根元素 Element root = doucument.getRootElement(); //獲得根元素的全部子節點 List<Element> elements = root.elements(); //遍歷全部子節點,獲取信息內容 for(Element element:elements){ requestMap.put(element.getName(),element.getText()); } //釋放資源 inputStream.close(); inputStream = null; return requestMap; } /** * 拓展xstream,使得支持CDATA塊 */ private static XStream xstream = new XStream(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { // 對那些xml節點的轉換增長CDATA標記 true增長 false反之 boolean cdata = false; /** * * @param name xml的元素節點若是不等於<xml>,例如爲<xml1>,那麼會將元素節點的首字母大寫,變爲:<Xml1> * @param clazz */ @SuppressWarnings("unchecked") public void startNode(String name, Class clazz) { if(!name.equals("xml")){ char[] arr = name.toCharArray(); if (arr[0] >= 'a' && arr[0] <= 'z') { //arr[0] -= 'a' - 'A'; //ASCII碼,大寫字母和小寫字符之間數值上差32 arr[0] = (char) ((int) arr[0] - 32); } name = new String(arr); } super.startNode(name, clazz); } /** * 給屬性值不爲浮點型或者整形的屬性加上<![CDATA["+屬性值+"]]>的標籤 * @param text */ @Override public void setValue(String text) { if(text!=null && !"".equals(text)){ //浮點型判斷 Pattern patternInt = Pattern.compile("[0-9]*(\\.?)[0-9]*"); //整型判斷 Pattern patternFloat = Pattern.compile("[0-9]+"); //若是是整數或浮點數 就不要加[CDATA[]了 if(patternInt.matcher(text).matches() || patternFloat.matcher(text).matches()){ cdata = false; }else{ cdata = true; } } super.setValue(text); } /** * 在xml節點中寫入節點內容 * @param writer * @param text */ protected void writeText(QuickWriter writer, String text) { /* if (cdata) { text = "<![CDATA["+text+"]]>"; } super.writeText(writer, text); */ if (cdata) { writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } }); /** * 文本消息對象轉換成xml * * @param textMessage 文本消息對象 * @return xml * 1.xstream的alias使用方法: * 1.1 做用:將序列化中的類全量名稱,用別名替換(沒有替換的屬性,則原樣輸出) * 1.2 使用方法:xstream.alias("blog", Blog.class); * * 2.xstream的aliasField * 2.1 做用:使用別名替代屬性名 * 2.2 使用方法:xstream.aliasField("author", Author.class, "name"); */ public static String textMessageToXml(TextMessage textMessage) { // xstream.alias("xml", textMessage.getClass()); xstream.alias("xml0", textMessage.getClass()); xstream.aliasField("funcFlag0", TextMessage.class, "funcFlag"); return xstream.toXML(textMessage); } }
須要轉化爲xml格式的參數類:
TextMessage.java
package com.example.gongzhong1.utils; /** * xml格式的內容做爲內部類顯示 */ public class TextMessage { private String toUserName; private String fromUserName; private String content; private Long createTime; private String msgType; private Integer funcFlag; public String getToUserName() { return toUserName; } public void setToUserName(String toUserName) { this.toUserName = toUserName; } public String getFromUserName() { return fromUserName; } public void setFromUserName(String fromUserName) { this.fromUserName = fromUserName; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Long getCreateTime() { return createTime; } public void setCreateTime(Long createTime) { this.createTime = createTime; } public String getMsgType() { return msgType; } public void setMsgType(String msgType) { this.msgType = msgType; } public Integer getFuncFlag() { return funcFlag; } public void setFuncFlag(Integer funcFlag) { this.funcFlag = funcFlag; } }
測試類:
package com.example.gongzhong1; import com.example.gongzhong1.utils.MessageUtil; import com.example.gongzhong1.utils.TextMessage; import org.junit.Test; public class MyTest { @Test public void testTextMessageToXml(){ TextMessage textMessage = new TextMessage(); textMessage.setToUserName("toUserName"); textMessage.setFromUserName("fromUserName"); textMessage.setContent("個人測試"); textMessage.setCreateTime(1348831860l); textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT); textMessage.setFuncFlag(0); String xml = MessageUtil.textMessageToXml(textMessage); System.out.println(xml); } }
輸出結果:
<Xml0> <ToUserName><![CDATA[toUserName]]></ToUserName> <FromUserName><![CDATA[fromUserName]]></FromUserName> <Content><![CDATA[個人測試]]></Content> <CreateTime>1348831860</CreateTime> <MsgType><![CDATA[text]]></MsgType> <FuncFlag0>0</FuncFlag0> </Xml0>