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>
相关文章
相关标签/搜索