xml/map轉換器
xml轉換: xml/map轉換器
xml合並: xml合並
snagit圖片:http://pan.baidu.com/s/1nuKJD13
git樣例: https://gitee.com/KingBoBo/XmlMapBeanTool/tree/master/XmlMapBeanTool
應用場景,為什么要把xml轉map?我直接用jdom,dom4j操作不行嗎?
- 如果你了解模板引擎(像velocity,mvel,httl等),會發現map形式在模板中取數可以如下所示直接取值,而xml字符串或dom則沒有這樣取值的便利和易理解特性.
<爸爸的小狗毛色>PACKET.MASTER_LIST.MASTER[1].DOG.COLOR</爸爸的小狗毛色>
以上語句中文翻譯為取得PACKET報文中的第2個MASTER主人擁有的狗狗的毛色.
- 而map.get("key")取值的性能遠勝於其它方式,針對報文中部分指定節點做類似於md5的重復性校驗(查重)也非常合適.
比如我們在庫中指定以下節點作為查重節點:
節點中文名 |
關鍵節點(查重節點) |
地址 | PACKET/FAMILY/ADDESS |
主人名 | PACKET/MASTER_LIST/MASTER/NAME |
之后可以通過以下方式取值
map.get("PACKET").get("FAMILY").get("ADDRESS") ;
map.get("PACKET").get("MASTER_LIST").get("MASTER").get("NAME"); // 考慮到有list,在取到get("MASTER")時做instanceof LIST,之后遍歷取出3個NAME
再把它們各自的value拼成 "紹興蘭亭MamaBabaSon", 最終后續來的報文也提取類似"杭州江干MamaBabaDaughter"進行歷史比對,就能馬上發現存在差異,至於有沒有差異的后續處理得根據業務需要了. (為了更高的性能先用length,再用equals,該點很重要,屬於性能優化點.)
設計思路
1. 有上下層次關系,必然遞歸最適合
2. 遞歸原則 : 一個map對應一個xml節點,key為當前xml節點名,value為當前xml節點子集
3.1 如果xml節點沒有子節點(葉子節點),那么map的key為xml節點名,value為xml節點文本內容
3.2 如果xml節點有一個子節點,那么map的key為xml節點名,value為xml節點子集
3.3.1 如果xml節點有多個子節點,對應map的key不存在(每一次),map的key為xml節點名,value為xml節點子集
3.3.2 如果xml節點有多個子節點,對應map的key已存在,且value為map類型(第二次),map的key為xml節點名,值從map類型轉為list,而list中添加2份當前xml節點子集
3.3.3 如果xml節點有多個子節點,對應map的key已存在,且value為list類型(第三/多次),那么直接加到list中去.
原始xml:
<?xml version="1.0" encoding="utf-8"?> <PACKET> <FAMILY> <COLOR>red</COLOR> <ADDRESS>紹興蘭亭</ADDRESS> </FAMILY> <MASTER_LIST> <MASTER> <NAME>Mama</NAME> <CAT> <COLOR>yellow</COLOR> </CAT> <CAT> <COLOR>grey</COLOR> </CAT> </MASTER> <MASTER> <NAME>Baba</NAME> <DOG> <COLOR>black</COLOR> </DOG> </MASTER> <MASTER> <NAME>Son</NAME> <RABBIT> <COLOR>white</COLOR> </RABBIT> </MASTER> </MASTER_LIST> </PACKET>
期望map:
{
PACKET={
FAMILY={
ADDRESS=紹興蘭亭,
COLOR=red
},
MASTER_LIST={
MASTER=[
{
NAME=Mama,
CAT=[
{
COLOR=yellow
},
{
COLOR=grey
}
]
},
{
NAME=Baba,
DOG={
COLOR=black
}
},
{
NAME=Son,
RABBIT={
COLOR=white
}
}
]
}
}
}
圖文思路解析
總map結構
葉子節點map結構
list層次結構第一次生成時
list層次結構第二次生成時
list層次結構第三/多次生成時
初版原碼
先理解初版,最重要版本.
package test.king; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; import test.king.tool.FileTool; public class XmlMapTool { public static void main(String[] args) { String input = FileTool.readStringFromFile("d://input.txt", "gbk"); Map<String, Object> map = xml2map(input); System.out.println("最終生成的map如下:\n"+map); } public static Map<String, Object> xml2map(String xml) { Document doc = null; try { doc = DocumentHelper.parseText(xml); } catch (DocumentException e) { e.printStackTrace(); } Map<String, Object> map = new HashMap<String, Object>(); if (doc == null) return map; Element rootElement = doc.getRootElement(); element2map(rootElement,map); return map; } public static Map element2map (Element outele,Map outmap) { System.out.println("當前位於"+outele.getName()+"節點"); List<Element> list = outele.elements();//必定返回0,1,2,3,....... 不會異常 int size = list.size(); if(size == 0){//當前節點是葉子節點 outmap.put(outele.getName(), outele.getTextTrim()); }else if(size == 1){ Map<String, Object> innermap = new HashMap<String, Object>(); Element ele1 = list.get(0); element2map(ele1,innermap); outmap.put(outele.getName(), innermap); }else if(size > 1){ Map<String, Object> innermap = new HashMap<String, Object>(); for(Element ele1 : list){ String eleName = ele1.getName(); Object obj = innermap.get(eleName);//獲取MASTER if(obj == null){//如果該MASTER不存在,現在有一個MASTER過來 element2map(ele1,innermap); }else{ if(obj instanceof java.util.Map){//如果沒有生成過list,把原來的單個map合並到新的list innermap.remove(eleName); List<Map> list1 = new ArrayList<Map>(); list1.add((Map) obj); Map<String, Object> map1 = new HashMap<String, Object>(); element2map(ele1,map1); list1.add((Map) map1.get(eleName)); innermap.put(eleName, list1); }else if(obj instanceof java.util.List){//如果已經生成過list element2map(ele1,innermap); ((List)obj).add(innermap); } } } outmap.put(outele.getName(), innermap); } return outmap; } }
優化后原碼
最終發現else if( size == 1 ){...... }的功能在 else if( size >1 ) 中已完全包含,所以可以舍掉 (size == 1) 這個多余處理.
且這里沒有使用Iterator<Element> eleItor = outele.elementIterator();用法,因為親自經過10000次大報文自測發現還是size()判斷更快.
size用法(推薦):
package test.king; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; import test.king.tool.FileTool; import test.king.tool.TLTimeContainer; public class XmlMapToolIngenious { public static void main(String[] args) { String input = FileTool.readStringFromFile("d://input.txt", "gbk"); Map<String, Object> map = null; TLTimeContainer.recordTime(); for(int i = 0 ; i < 10000 ; i++){ map = xml2map(input); } TLTimeContainer.recordTime(); TLTimeContainer.print(); System.out.println("最終生成的map如下:\n"+map); } public static Map<String, Object> xml2map(String xml) { Document doc = null; try { doc = DocumentHelper.parseText(xml); } catch (DocumentException e) { e.printStackTrace(); } Map<String, Object> map = new HashMap<String, Object>(); if (doc == null) return map; Element rootElement = doc.getRootElement(); element2map(rootElement,map); return map; } public static Map element2map (Element outele,Map outmap) { List<Element> list = outele.elements();//必定返回0,1,2,3,....... 不會異常 int size = list.size(); if(size == 0){//當前節點是葉子節點(outele如果為葉子節點,是不可能有子節點的,因為它里面是純文本) outmap.put(outele.getName(), outele.getTextTrim()); }else{ Map<String, Object> innermap = new HashMap<String, Object>(); for(Element ele1 : list){ String eleName = ele1.getName(); Object obj = innermap.get(eleName);//獲取MASTER if(obj == null){//如果該MASTER不存在,現在有一個MASTER過來 element2map(ele1,innermap); }else{ if(obj instanceof java.util.Map){//如果沒有生成過list,把原來的單個map合並到新的list List<Map> list1 = new ArrayList<Map>(); list1.add((Map) innermap.remove(eleName)); element2map(ele1,innermap); list1.add((Map) innermap.remove(eleName)); innermap.put(eleName, list1); }else{//如果不是map,必然是list,只有這兩種情況,所以不再else if 條件判斷 element2map(ele1,innermap); ((List)obj).add(innermap); } } } outmap.put(outele.getName(), innermap); } return outmap; } }
同事施明方法,比我的更優雅更簡潔更高效(強烈推薦)
1. 有上下層次關系,必然遞歸最適合
2. 遞歸原則 : 一個map對應一個xml節點,key為當前xml節點名,value為當前xml節點子集
3. 如果xml節點沒有子節點(葉子節點),那么map的key為xml節點名,value為xml節點內文本內容
4. 如果xml節點有子節點,那么讓子節點和新生成子map去遞歸(子節點和新map會關聯到一起)
5. 然后就要把子map或子list置入上一層map中
5.2如果子map第一次生成,用上層map直接添加子map
5.3如果子map第二次生成,用新list把兩份子map添加,再用上一層map添加新list
5.4如果子map第三/多次,用list把第三/多次子map直接添加
public void element2map(Element elmt, Map<Object, Object> map) { if (null == elmt) { return; } String name = elmt.getName(); if (elmt.isTextOnly()) { map.put(name, elmt.getText()); } else { Map<Object, Object> mapSub = new HashMap<Object, Object>(); List<Element> elements = (List<Element>) elmt.elements(); for (Element elmtSub : elements) { element2map(elmtSub, mapSub); } Object first = map.get(name); if (null == first) { map.put(name, mapSub); } else { if (first instanceof List<?>) { ((List) first).add(mapSub); } else { List<Object> listSub = new ArrayList<Object>(); listSub.add(first); listSub.add(mapSub); map.put(name, listSub); } } } }
微整后
/** * * @param elmt 當前元素 * @param map 主鍵為當前元素的節點名,值為當前元素的所有直接子元素 */ public static void element2map(Element elmt, Map<String, Object> map) { if (null == elmt) { return; } String name = elmt.getName(); if (elmt.isTextOnly()) { map.put(name, elmt.getText()); } else { Map<String, Object> mapSub = new HashMap<String, Object>(); List<Element> elements = (List<Element>) elmt.elements(); for (Element elmtSub : elements) { element2map(elmtSub, mapSub); } Object first = map.get(name); if (null == first) { map.put(name, mapSub); } else { if (first instanceof List<?>) { ((List) first).add(mapSub); } else { List<Object> listSub = new ArrayList<Object>(); listSub.add(first); listSub.add(mapSub); map.put(name, listSub); } } } }
iterator用法(不推薦):
因為在10000次循環時間對比中,還沒有size()判斷比iterator()快1秒左右. 起碼在dom4j中是這樣的情況.

package test.king; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; import test.king.tool.FileTool; import test.king.tool.TLTimeContainer; public class XmlMapToolIngenious { public static void main(String[] args) { String input = FileTool.readStringFromFile("d://input.txt", "gbk"); Map<String, Object> map = null; TLTimeContainer.recordTime(); for(int i = 0 ; i < 10000 ; i++){ map = xml2map(input); } TLTimeContainer.recordTime(); TLTimeContainer.print(); System.out.println("最終生成的map如下:\n"+map); } public static Map<String, Object> xml2map(String xml) { Document doc = null; try { doc = DocumentHelper.parseText(xml); } catch (DocumentException e) { e.printStackTrace(); } Map<String, Object> map = new HashMap<String, Object>(); if (doc == null) return map; Element rootElement = doc.getRootElement(); element2map(rootElement,map); return map; } public static Map element2map (Element outele,Map outmap) { List<Element> list = outele.elements();//必定返回0,1,2,3,....... 不會異常 int size = list.size(); Iterator<Element> eleItor = outele.elementIterator(); if(!eleItor.hasNext()){//當前節點是葉子節點(outele如果為葉子節點,是不可能有子節點的,因為它里面是純文本) outmap.put(outele.getName(), outele.getTextTrim()); }else{ Map<String, Object> innermap = new HashMap<String, Object>(); for(;eleItor.hasNext();){ Element ele1 = eleItor.next(); String eleName = ele1.getName(); Object obj = innermap.get(eleName);//獲取MASTER if(obj == null){//如果該MASTER不存在,現在有一個MASTER過來 element2map(ele1,innermap); }else{ if(obj instanceof java.util.Map){//如果沒有生成過list,把原來的單個map合並到新的list List<Map> list1 = new ArrayList<Map>(); list1.add((Map) innermap.remove(eleName)); element2map(ele1,innermap); list1.add((Map) innermap.remove(eleName)); innermap.put(eleName, list1); }else{//如果不是map,必然是list,只有這兩種情況,所以不再else if 條件判斷 element2map(ele1,innermap); ((List)obj).add(innermap); } } } outmap.put(outele.getName(), innermap); } return outmap; } }
相關類
FileTool.java工具類 (用於讀取文件內容)
備用

// IteRator<Element> eles = root.elementIterator(); // while(eles.hasNext()){ // // }
本文純原創,開源精神,歡迎轉載,請說明出處 by 金墨痴 http://www.cnblogs.com/whatlonelytear/p/5797979.html