Xstream XML文件互轉JAVA對象的利器

公司給工具作個接口測試,工具返回給咱們文件格式爲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>
相關文章
相關標籤/搜索