公司给工具做个接口测试,工具返回给我们文件格式为xml,我们平台采用JAVA开发,为了今后的数据持久化 和 查看结果的方便,应该将XML转换为Object,这样接触到了XStream这个类库,虽然小,但功能着实强大。
网上有一篇灰常详细的文章对XStream进行介绍http://www.cnblogs.com/hoojo/archive/2011/04/22/2025197.html,感谢博主。
另外这里对其进行一些补充,主要是在解析XML中遇到过一些不太符合常规的XML格式,这样就需要一些不同常规的处理方式来解决:
一、Converter接口的使用
话说有这么个响应文件需要进行解析
"
<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对象,其中包含
engineName,cPU,mEM,dISK,List<Func>属性,其中Func对象对应<Func/>结点。按照普通的方法,无法将其中<Func/>结点的text值赋值给Func对象。
这时Converter就派上用场了:首先定义好各类
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");
这样就可以进行解析了!
二、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>
来源:oschina
链接:https://my.oschina.net/u/2331760/blog/3042773