最近由於進行微信公眾號開發,了解了一下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
歡迎大家下載,轉載~~~