xml/map轉換器,遞歸設計思路【純原】


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;
    }

    
}
View Code

 

 相關類

FileTool.java工具類  (用於讀取文件內容)

 備用

    
    
    
    
    
//     IteRator<Element> eles = root.elementIterator(); 
//     while(eles.hasNext()){ 
//         
         
//     }
View Code

 

本文純原創,開源精神,歡迎轉載,請說明出處 by 金墨痴 http://www.cnblogs.com/whatlonelytear/p/5797979.html


免責聲明!

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



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