公司给工具作个接口测试,工具返回给咱们文件格式为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>