java之xml字符串轉map、object轉xml字符串


最近由於進行微信公眾號開發,了解了一下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

歡迎大家下載,轉載~~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM