最近由於進行微信公眾號開發,了解了一下java的一些xml操作。
所以本篇文章簡單介紹xml字符串轉map和object轉xml字符串。
由於與微信交互的過程中一些數據微信是以xml字符串形式發送到我們服務器,然后將處理好的對象轉成xml字符串發送到微信服務器。
所以需要封裝個專門的工具類來處理,我建了一個類XmlUtil,用來實現上述功能。
jar包准備:
<!-- 解析xml --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.9</version> </dependency>
xml字符串轉map:
xml字符串的獲取:從請求request的輸入流中讀取Document文檔對象。
用SAXReader獲取List<Element>,遍歷,將當前迭代元素的name和text作為鍵值對存入map。
具體代碼如下:
/** * xml轉map * * @param request * @return * @throws IOException */ public static Map<String, String> xml2Map(HttpServletRequest request) throws IOException { Map<String, String> map = new HashMap<String, String>(); SAXReader reader = new SAXReader(); InputStream is = null; Document doc = null; try { is = request.getInputStream(); doc = reader.read(is); Element root = doc.getRootElement(); List<Element> list = root.elements(); for (Element e : list) { map.put(e.getName(), e.getText()); } } catch (IOException e) { e.printStackTrace(); } catch (DocumentException e) { e.printStackTrace(); } finally { is.close(); } return map; }
接下來是XStream部分(比較重要):
private static String cdata_prefix = "<![CDATA[";
private static String cdata_suffix = "]]>";
/**
* 文本消息對象轉Xml
*
* @param 對象
* @param 對象的類型
* @return
*/
public static String textMessage2Xml(Object object,Class<?> objectTargetClass) {
XStream xStream = new XStream(new XppDriver() {
/**
* 對xml標簽里面值含有CDATA會被轉義,比如<會被轉義成<
* 以下代碼為了能加入不被轉義的CDATA
*/
@Override
public HierarchicalStreamWriter createWriter(Writer out) {
PrettyPrintWriter ppw = new PrettyPrintWriter(out) {
Boolean isCdata = false;
Class<?> targetClazz = null;
Class<?> superClass = null;
List<String> super_class_field_names = new ArrayList<String>();
List<String> target_class_field_names = new ArrayList<String>();
@Override
public void startNode(String name, @SuppressWarnings("rawtypes") Class clazz) {
super.startNode(name, clazz);
/**
* System.out.println(textMessage.getCreateTime()+",name:"+name+",clazz:"+clazz);
* 輸出結果
* 173413984,name:xml,clazz:class com.entity.TextMessage
* 173413984,name:ToUserName,clazz:class java.lang.String
* 173413984,name:FromUserName,clazz:class java.lang.String
* 173413984,name:CreateTime,clazz:class java.lang.String
* 173413984,name:MsgType,clazz:class java.lang.String
* 173413984,name:Content,clazz:class java.lang.String
*
* name為xml時,clazz為xStream.alias("xml", textMessage.getClass())聲明的類
*/
try {
if ("xml".equals(name)) {
initParam(clazz);
} else {
if (super_class_field_names.contains(name)) {
isCdata=superClass.getDeclaredField(StringUtil.firstLowerCase(name)).isAnnotationPresent(CDATA_Annotaion.class);
} else if (target_class_field_names.contains(name)) {
isCdata=targetClazz.getDeclaredField(StringUtil.firstLowerCase(name)).isAnnotationPresent(CDATA_Annotaion.class);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void initParam(@SuppressWarnings("rawtypes") Class clazz) {
targetClazz = clazz;
// 目標類屬性名保存
Field[] targetFields = targetClazz.getDeclaredFields();
for (Field f : targetFields) {
target_class_field_names.add(StringUtil.firstUpCase(f.getName()));
}
// 父類屬性名保存到list中
superClass = targetClazz.getSuperclass();
if (!superClass.equals(Object.class)) {
Field[] superFields = superClass.getDeclaredFields();
for (Field f : superFields) {
super_class_field_names.add(StringUtil.firstUpCase(f.getName()));
}
}
}
@Override
protected void writeText(QuickWriter writer, String text) {
if (isCdata) {
writer.write(cdata_prefix + text + cdata_suffix);
} else {
writer.write(text);
}
}
};
return ppw;
}
});
xStream.alias("xml", objectTargetClass);
//開啟注解轉換(別名轉換)
xStream.processAnnotations(objectTargetClass);
return xStream.toXML(object);
}
解釋下上面代碼:
XStream xStream = new XStream(new XppDriver() {...})
這一步中間new了個XppDriver,關於XppDriver的介紹,我查找資料后,理解為一種解析輸入輸出的驅動。

因為我們要在寫的時候做點處理,所以重寫了public HierarchicalStreamWriter createWriter(Writer out){...}方法:
PrettyPrintWriter ppw = new PrettyPrintWriter(out) {...}中重寫了
@Override
public void startNode(String name, @SuppressWarnings("rawtypes") Class clazz) {...}方法
和
@Override
protected void writeText(QuickWriter writer, String text) {...}方法
writeText是最后寫處理,比如我要根據isCdata判斷是否添加<![CDATA[]]>,isCdata為true就添加,false不添加
startNode是在節點開始的時候加入自己的代碼邏輯,比如我在這邊判斷實體類的屬性上面是否有@CDATA_Annotation,如果有我就將isCdata賦值為true,反之false。
每條實體類屬性都是先startNode->writeText,比如實體類有3條屬性,在startNode時候添加一條system.out.printlin語句看情況,就會發現輸出3次。
/**
* System.out.println(textMessage.getCreateTime()+",name:"+name+",clazz:"+clazz);
* 輸出結果
* 173413984,name:xml,clazz:class com.entity.TextMessage
* 173413984,name:ToUserName,clazz:class java.lang.String
* 173413984,name:FromUserName,clazz:class java.lang.String
* 173413984,name:CreateTime,clazz:class java.lang.String
* 173413984,name:MsgType,clazz:class java.lang.String
* 173413984,name:Content,clazz:class java.lang.String
*
* name為xml時,clazz為xStream.alias("xml", textMessage.getClass())聲明的類
*/
我例子中的輸出結果,類是TextMessage,里面有條屬性content,剩下4條屬於繼承來的,做了一些處理把父類的也取到了,173413984是TextMessage類的createTime屬性的值。
可以看出后面6條都是屬於同一個對象,至於第一條name為xml是因為下面

生聲明了類的別名為xml
解釋完了上面代碼,現在說下注意事項:
可能有細心的人注意到了,我用了首字母大小寫轉換,而且控制台輸出的時候name是大寫,因為我在實體類上加了XStream別名

所以startNode傳進來的name會是首字母大寫的形式,所以在反射的時候將其轉換成了小寫,為什么用別名是因為不想將實體類屬性名變成首字母大寫,那樣不規范(普通類的類名就是大寫的,會混亂),所以在不改變實體類的情況下用了別名。
如果用了別名,一定要記住在toXML方法之前一定要開啟注解轉換

StringUtil(首字母大寫和首字母小寫的方法):
public class StringUtil {
public static String firstUpCase(String str) {
String result=str.substring(0,1).toUpperCase()+str.substring(1,str.length());
return result;
}
public static String firstLowerCase(String str){
String result=str.substring(0,1).toLowerCase()+str.substring(1,str.length());
return result;
}
}
注解CDATA_Annotaion:
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD })
@Inherited
public @interface CDATA_Annotaion {
}
有不同的歡迎提問,有錯歡迎指出!謝謝大家瀏覽!
ps:每天記錄一點,就算寫的很差,以后自己找起來也方便,也能記得更牢。
附上github:https://github.com/617355557/code_pianduan/blob/master/XmlUtil.java
歡迎大家下載,轉載~~~
