美文网首页必看必学必完成Java学习笔记
解析xml格式文件(不用第三方库函数)

解析xml格式文件(不用第三方库函数)

作者: 默默奋斗的木头人 | 来源:发表于2017-06-16 18:05 被阅读18次

    题目:

    将下面xml格式文件,按树状结构进行输出,格式如书本目录,条件是不能用第三方库,比如什么dom,sax,pull解析方式,一句话,把里面的节点自己慢点抠出来吧。
    (纯属自娱自乐,有问题的地方或者有更好的想法,欢迎交流,谢谢,qq:413686520)

    <people  >
        <person id="001">  
            <name>XY1</name>  
            <age>22</age>  
        </person>
    
        <name key1="fsefsef" key2="gdrhr" />
        <person id="002">  
            <name>XY2</name>
            <age>22</age>
            <grade>
                <math>98</math>
                <english>100</english>
                <music>28</music>
            </grade>
        </person>
        <wenzhang key1="fsefsef" key2="gdrhr" />
        <life key1="fsefsef" key2="gdrhr" />
    
    </people>
    

    对于这个题目,最开始我对出题人的要求搞错了,以为只要节点名字,节点深度,最后输出成目录结构就行。所以,我就悲剧了,我最开始是用in.read()一个一个读到内存中,按字符来解析的,可以想象一下最后是多么艰难的写出来的,花了4个小时,可能时间更长,早上没有吃饭,写完的时候,已经饿的肚子疼了。一个字符一个字符的解析,竟然让我搞出来了。最开始的想法决定了算法最终的难度。下面给出这两天重新写过后的结果:

    图片.png

    其中,我们拿grade节点说明一下,它的节点深度为3,它有孩子节点mat,h,english,music,其他的节点可能还存在key:value键值对等等(直接看图吧)。

    思路:

    首先,一次读取一行,并且从读取的内容中找到第一个右括号‘>’,这样我们最后需要处理的内容都会变为如下几种形式:

    (1).***<******>
    (2).***<******/>
    (3).***</******>
    

    注意:先规定下本文节点的格式
    例如


    图片.png

    对于上面的三种状态,对于‘<’左边的只可能是节点的text文本值,然后对于“<>”,里面只可能是节点名字,键值对,节点结束标志。

    首先构建我们的节点,如下所示:

    package com.lifestudy.stdy.lifestudy.readxml;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * Created by lj on 2017/6/16.
     */
    
    public class LjXMLNode {
        int id;//唯一标识
        String name;//节点的名字
        List<Integer> children = new ArrayList();//当前节点的孩子节点
        int parent = -1;//默认父节点,用id表示
        Map<String,String> mapKeyValue = new HashMap();//当前节点的key:value
        String text="";//当前节点的文本值
        int level;//当前节点的深度
    }
    
    

    然后,开始开始遍历我们的xml文件:

    public void readXMLFile(InputStream inputStream){
            try {
                InputStreamReader inputStreamReader = new InputStreamReader(putStream);
                BufferedReader in = new BufferedReader(inputStreamReader);
                System.out.println("开始输入:");
                StringBuffer sb = new StringBuffer();
                String s;
                String rest = "";
                while( ( s = in.readLine() ) != null ){
                    s = s.trim();
                    s = rest + s;
    
                    int start = 0;
                    for( int i = 0; i < s.length(); i++ ){
                        if( s.charAt(i) == '>'){
                            process(s.substring(start,i+1));
                            start = i + 1;
                        }
                    }
                    if( start >= s.length() ){//說明處理到最後一個字符了
                        //do nothing
                    }else{
                        rest = s.substring(start,s.length());
                    }
                }
    //            System.out.println( sb.toString() );
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    

    然后,在process()方法中处理最开始的三种情况:
    说明:对于'<'左边的字符都是节点的text值,对于‘<’右边的第一个单词肯定是节点的name,当我们读取完name时,我们创建一个LjXMLNode节点node,赋值node.name,最后,入栈的并将其父节点node.parent指向栈顶元素,同时让栈顶元素的孩子节点加1。然后是读取节点的key:value(这个可以根据是否有等号来判断),并且更新node的值。最后当前节点只可能以右括号'>'或者'/>'两种形式结尾,当我们读取到“/”时,将当前节点出栈(同时,如果想对节点做操作,也是在出栈时,因为出栈的节点元素是完整的,它肯定是遍历完成了,本文是将出栈的元素保存到一个map中了,其中key是id,value是LjXMLNode)。

    /**
         * 處理
         * 主要是對stack的操作
         */
        private void process(String s){
    
            String nodeName = "";
            String text = "";
    
            for( int i = 0; i < s.length();i++ ){
                if( s.charAt(i) == '<'){
    
                    text = s.substring(0,i);
                    if( mStack.size() > 0){
                        LjXMLNode topNode = mStack.peek();
                        topNode.text = text;
                    }
    
                    if( s.charAt(i+1) != '/'){
    
                        nodeName = findNodeName(s.substring(i+1));
                        //push
                        LjXMLNode node = new LjXMLNode();
                        node.id = ID_0++;
                        node.name = nodeName;
                        if( mStack.size() > 0){
                            LjXMLNode parentNode = mStack.peek();
                            node.parent = parentNode.id;
                            parentNode.children.add(node.id);
                        }
                        mStack.push(node);
    
                        for( int j = i+1; j < s.length();j++ ){
                            if( s.charAt(j) == '='){//key:value
                                String key = findFirstLeftWord(s,j);
                                String value = findFirstRightWord(s,j);
                                if( key != ""){
                                    LjXMLNode curNode = mStack.peek();
                                    curNode.mapKeyValue.put(key,value);
                                }
                            }
                        }
    
                        for( int j = i+2; j < s.length();j++ ){
                            if( s.charAt(j) == '/'){
                                LjXMLNode shuchuNode =  mStack.pop();
                                shuchuNode.level = mStack.size() + 1;
                                mFinalNodes.put(shuchuNode.id,shuchuNode);
                                System.out.println("shuchuNode:" + "id:"+shuchuNode.id + " name:"+shuchuNode.name
                                        + " key_count:"+shuchuNode.mapKeyValue.size()
                                        + " text:" + shuchuNode.text
                                        +"  parent:"+ shuchuNode.parent);
                            }
                        }
    
                    }else {
                        //pop
                        LjXMLNode shuchuNode =  mStack.pop();
                        shuchuNode.level = mStack.size() + 1;
                        mFinalNodes.put(shuchuNode.id,shuchuNode);
                        System.out.println("shuchuNode:" + "id:"+shuchuNode.id + " name:"+shuchuNode.name
                                + " key_count:"+shuchuNode.mapKeyValue.size()
                                + " text:" + shuchuNode.text
                                +"  parent:"+ shuchuNode.parent);
                    }
                    break;
                }
            }
        }
    

    最后是对节点的遍历,因为处理后的节点,最后会存储为成森林的树状结构,如下所示:

    图片.png

    所以,这块采用树的深度优先遍历算法。思想很简单,构建一个栈结构,首先将根节点入栈,也就是图中的A节点,然后开始遍历,先将根节点A出栈,同时访问根节点A,并且将其孩子节点入栈(注意此时的入栈顺序是从最右边的孩子开始往左遍历,也就是说越在左边的孩子应该越早访问到)。后面仿照根节点A出栈的同时访问,并且将其孩子节点入栈,代码如下所示:

    public void depthLjFirst(){
            Stack<LjXMLNode> depthStack = new Stack();
            LjXMLNode head = mFinalNodes.get(0);
            depthStack.push(head);
            while ( depthStack.size()> 0 ){
                LjXMLNode cur = depthStack.pop();
                visit(cur);
                for( int i = cur.children.size(); i > 0;i-- ){
                    int childId = cur.children.get(i-1);
                    depthStack.push( mFinalNodes.get(childId) );
                }
            }
        }
    

    最后完整的代码如下所示:

    package com.lifestudy.stdy.lifestudy.readxml;
    
    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    import java.util.Stack;
    
    /**
     * Created by lj on 2017/6/16.
     */
    
    public class LjXMLParser {
        private Stack<LjXMLNode> mStack = new Stack();
        Map<Integer,LjXMLNode> mFinalNodes = new HashMap();
        private int ID_0 = 0;
        public void readXMLFile(InputStream in1){
            try {
                InputStreamReader inputStreamReader = new InputStreamReader(in1);
                BufferedReader in = new BufferedReader(inputStreamReader);
                System.out.println("开始输入:");
                StringBuffer sb = new StringBuffer();
                String s;
                String rest = "";
                while( ( s = in.readLine() ) != null ){
                    s = s.trim();
                    s = rest + s;
    
                    int start = 0;
                    for( int i = 0; i < s.length(); i++ ){
                        if( s.charAt(i) == '>'){
                            process(s.substring(start,i+1));
                            start = i + 1;
                        }
                    }
                    if( start >= s.length() ){//說明處理到最後一個字符了
                        //do nothing
                    }else{
                        rest = s.substring(start,s.length());
                    }
                }
    //            System.out.println( sb.toString() );
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 處理
         * 主要是對stack的操作
         */
        private void process(String s){
    
            String nodeName = "";
            String text = "";
    
            for( int i = 0; i < s.length();i++ ){
                if( s.charAt(i) == '<'){
    
                    text = s.substring(0,i);
                    if( mStack.size() > 0){
                        LjXMLNode topNode = mStack.peek();
                        topNode.text = text;
                    }
    
                    if( s.charAt(i+1) != '/'){
    
                        nodeName = findNodeName(s.substring(i+1));
                        //push
                        LjXMLNode node = new LjXMLNode();
                        node.id = ID_0++;
                        node.name = nodeName;
                        if( mStack.size() > 0){
                            LjXMLNode parentNode = mStack.peek();
                            node.parent = parentNode.id;
                            parentNode.children.add(node.id);
                        }
                        mStack.push(node);
    
                        for( int j = i+1; j < s.length();j++ ){
                            if( s.charAt(j) == '='){//key:value
                                String key = findFirstLeftWord(s,j);
                                String value = findFirstRightWord(s,j);
                                if( key != ""){
                                    LjXMLNode curNode = mStack.peek();
                                    curNode.mapKeyValue.put(key,value);
                                }
                            }
                        }
    
                        for( int j = i+2; j < s.length();j++ ){
                            if( s.charAt(j) == '/'){
                                LjXMLNode shuchuNode =  mStack.pop();
                                shuchuNode.level = mStack.size() + 1;
                                mFinalNodes.put(shuchuNode.id,shuchuNode);
                                System.out.println("shuchuNode:" + "id:"+shuchuNode.id + " name:"+shuchuNode.name
                                        + " key_count:"+shuchuNode.mapKeyValue.size()
                                        + " text:" + shuchuNode.text
                                        +"  parent:"+ shuchuNode.parent);
                            }
                        }
    
                    }else {
                        //pop
                        LjXMLNode shuchuNode =  mStack.pop();
                        shuchuNode.level = mStack.size() + 1;
                        mFinalNodes.put(shuchuNode.id,shuchuNode);
                        System.out.println("shuchuNode:" + "id:"+shuchuNode.id + " name:"+shuchuNode.name
                                + " key_count:"+shuchuNode.mapKeyValue.size()
                                + " text:" + shuchuNode.text
                                +"  parent:"+ shuchuNode.parent);
                    }
                    break;
                }
            }
        }
    
        /**
         * 找到右邊的第一個單詞,只能以空格或者是右括號“>”結尾
         */
        private String findNodeName(String s){
            int start,end;
            end = -1;
            start = -1;
            for( int i = 0; i < s.length(); i++){
                if( s.charAt(i) != ' '){ //找到左边的第一个字母
                    start = i;
    //                System.out.println("nodeName_start:" + start);
                    break;
                }
            }
            if( start < s.length() && start >= 0){
                for( int i = start + 1; i < s.length(); i++ ){
                    if( s.charAt(i) == ' ' || s.charAt(i) == '>'){
                        end = i;
                        break;
                    }
                }
            }
    
            if( start <= end && start != -1){
                return s.substring(start,end);
            }
            return "";
        }
    
        /**
         * 找到下标为loc左边第一个单词
         */
        private String findFirstLeftWord(String s,int loc){
    //        System.out.println("开始解析:" + s + "###loc:" + loc);
            String key= "";
            int start,end;
            end = -1;
            start = -1;
            for( int i = loc-1; i > 0; i--){
                if( s.charAt(i) != ' '){ //找到左边的第一个字母
                    end = i;
    //                System.out.println("end:" + end);
                    break;
                }
            }
    //        System.out.println("end解析完成");
            if( end < loc && end > 0){
                for( int i = end; i > 0; i--){
                    if( s.charAt(i) == ' '){ //找到左边的第一个空格
                        start = i+1;
    //                    System.out.println("start:" + start);
                        break;
                    }
                }
            }
            if( start <= end && start != -1){
                return s.substring(start,end+1);
            }
            return "";
        }
    
        /**
         * 找到loc右边第一个双引号包括的单词
         */
        private String findFirstRightWord(String s,int loc){
    //        System.out.println("s:" + s);
            String value= "";
            int start,end;
            end = -1;
            start = -1;
            for( int i = loc + 1; i < s.length(); i++){
                if( s.charAt(i) == '"'){ //找到右边的第一个双引号
                    start = i;
    //                System.out.println("start:" + start);
                    break;
                }
            }
            if( start > loc && start < s.length()){
                for( int i = start+1; i < s.length(); i++){
                    if( s.charAt(i) == '"'){ //找到右边的第一个双引号
                        end = i+1;
    //                    System.out.println("end:" + end);
                        break;
                    }
                }
            }
            if( start <= end && start != -1){
                return s.substring(start,end);
            }
            return "";
        }
    
        public void depthLjFirst(){
            Stack<LjXMLNode> depthStack = new Stack();
            LjXMLNode head = mFinalNodes.get(0);
            depthStack.push(head);
            while ( depthStack.size()> 0 ){
                LjXMLNode cur = depthStack.pop();
                visit(cur);
                for( int i = cur.children.size(); i > 0;i-- ){
                    int childId = cur.children.get(i-1);
                    depthStack.push( mFinalNodes.get(childId) );
                }
            }
        }
    
        private void visit(LjXMLNode node){
            StringBuffer sb = new StringBuffer();
            for( int i = 0; i < node.level; i++){
                sb.append("  ");//三个空格
            }
            sb.append("" + node.level);
            sb.append("  "+ node.name);
            Set<Map.Entry<String, String>> set2=node.mapKeyValue.entrySet();
            for (Iterator<Map.Entry<String, String>> iterator = set2.iterator(); iterator.hasNext();) {
                Map.Entry<String, String> entry = (Map.Entry<String, String>) iterator.next();
                String key=entry.getKey();
                String valueString=entry.getValue();
                sb.append("  "+ key + "=" + valueString );
            }
            // TODO: 2017/6/16 此处用“!=”来判断text是否为空会出错 (why?)
            if( node.text != null&& !node.text.equals("")  ){
                sb.append("  text:" + node.text);
            }
    
            System.out.println( sb.toString());
        }
    }
    
    

    相关文章

      网友评论

        本文标题:解析xml格式文件(不用第三方库函数)

        本文链接:https://www.haomeiwen.com/subject/aormqxtx.html