返回通过正则表达式......登陆

通过正则表达式实现简单xml文件解析

阿神2016-11-11 17:09:09741

这是我通过正则表达式实现的xml文件解析工具,有些XHTML文件中包含特殊符号,暂时还无法正常使用。

设计思路:常见的xml文件都是单根树结构,工具的目的是通过递归的方式将整个文档树装载进一个Node对象。xml文档树上的每一个节点都能看做一个Node对象,它拥有title、attribute和text三个自身变量以及一个childrenNode集合用来存放子节点,使用正则表达式完整装载。

一、编写Node类

Node对象是文档解析的基础,最终可以通过对象的不同属性实现对文档信息的访问。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

import java.io.Serializable;

import java.util.HashMap;

import java.util.Iterator;

import java.util.LinkedList;

import java.util.List;

import java.util.Map;

import java.util.Map.Entry;

 

public class Node implements Serializable {

    // 可以对Node对象持久化保存

    private static final long serialVersionUID = 1L;

    private int id;

    // 节点类型

    private String title;

    // 节点内容

    private String text;

    // 节点属性集合

    private Map<String, String> attributes = new HashMap<String, String>();

    // 子节点集合

    private List<Node> childNodes = new LinkedList<Node>();

 

    public int getId() {

        return id;

    }

 

    public void setId(int id) {

        this.id = id;

    }

 

    public String getTitle() {

        return title;

    }

 

    public void setTitle(String title) {

        this.title = title;

    }

 

    public Map<String, String> getAttribute() {

        return attributes;

    }

 

    public void setAttribute(Map<String, String> attribute) {

        this.attributes = attribute;

    }

 

    public String getText() {

        return text;

    }

 

    public void setText(String text) {

        this.text = text;

    }

 

    public List<Node> getChildNode() {

        return childNodes;

    }

 

    public void setChildNode(List<Node> childNode) {

        this.childNodes = childNode;

    }

 

    // 将属性集合转换成一条完整的字符串

    private String attrToString() {

        if (attributes.isEmpty()) {

            return "";

        }

        Iterator<Entry<String, String>> its = attributes.entrySet().iterator();

        StringBuffer buff = new StringBuffer();

        while (its.hasNext()) {

            Entry<String, String> entry = its.next();

            buff.append(entry.getKey() + "=\"" + entry.getValue() + "\" ");

        }

        return " " + buff.toString().trim();

    }

 

    // 输出完整的节点字符串也用到了递归

    @Override

    public String toString() {

        String attr = attrToString();

        if (childNodes.isEmpty() && text == null) {

            return "<" + title + attr + "/>\n";

        else if (childNodes.isEmpty() && text != null) {

            return "<" + title + attr + ">\n" + text + "\n" "</" + title + ">\n";

        else {

            StringBuffer buff = new StringBuffer();

            buff.append("<" + title + attr + ">\n");

            if (!text.isEmpty()) {

                buff.append(text + "\n");

            }

            for (Node n : childNodes) {

                buff.append(n.toString());

            }

            buff.append("</" + title + ">\n");

            return buff.toString();

        }

    }

}

 

Node.java

二、创建接口

把文档的读取和分析抽象成接口方便今后替换实现。

过滤器:读取文档的字符流并删除注释的部分。这些信息通常是提供给人阅读的,程序分析直接忽略。

1

2

3

4

5

6

7

8

9

10

11

12

/*

 * 过滤器的作用是删除xml文件中不重要的部分。

 * 通常都是一些注释性文字,不需要被机器解析。

 */

public interface XmlFilter {

    String filter();

 

    // 提供自定义正则表达式,识别符合过滤条件的字符串

    String filter(String[] regex);

}

 

XmlFilter.java

解析器:将一个父节点解析成多条子节点的字符串。如果返回值为null,代表当前节点下不存在可以继续解析的对象。

1

2

3

4

5

6

7

8

9

10

11

import java.util.List;

/*

 * 解析器可以对一段完整的父节点字符串提供解析服务。

 * 将一条父节点的字符串解析成为多条子节点字符串

 */

public interface XmlParser {

    // 解析一段父节点,返回子节点字符串

    List<String> parser(String str);

}

 

XmlParser.java

三、根据接口编写实现类

回车、换行、制表符以及各种注释部分的内容都被删除,简化字符输出。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

import java.io.BufferedReader;

import java.io.File;

import java.io.FileNotFoundException;

import java.io.FileReader;

import java.io.IOException;

 

public class SimpleXmlFilter implements XmlFilter {

    private String text;

    // 常用的过滤正则表达式

    public final static String[] REG = { "\t""<\\?.*?\\?>""<!.*?>""<%.*?%>""\\s{2,}" };

 

    // 读取xml文档返回字符串

    public SimpleXmlFilter(File file) throws IOException {

        BufferedReader in = new BufferedReader(new FileReader(file));

        StringBuffer buff = new StringBuffer();

        String temp = null;

        while ((temp = in.readLine()) != null) {

            buff.append(temp);

        }

        in.close();

        text = buff.toString().trim();

    }

 

    @Override

    public String filter() {

        return filter(REG);

    }

 

    @Override

    public String filter(String[] regex) {

        String result = text;

        for (String reg : regex) {

            result = result.replaceAll(reg, "");

        }

        return result;

    }

 

}

 

SimpleXmlFilter.java

主要是通过正则表达式区分一个节点内部的子节点,考虑到节点的类型我将它们分为自闭合与非自闭合两种类型。<title attributes .../>这样的节点属于自闭合类型,它们不包含子节点和text属性,它们属于文档树的叶子节点。<title attributes ...>text ...</title>这样的节点属于非自闭合类型,它们属于文档树的分支节点。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

import java.util.ArrayList;

import java.util.List;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

 

public class SimpleXmlParser implements XmlParser {

 

    @Override

    public List<String> parser(String text) {

        List<String> childrenDocs = new ArrayList<String>();

        // 捕获根节点中间的文本

        Pattern p = Pattern.compile("<.*?>(.*)</.*?>");

        Matcher m = p.matcher(text);

        if (m.matches()) {

            String inner = m.group(1);

            // 匹配节点字符串

            p = Pattern.compile("<(.*?)>");

            m = p.matcher(inner);

            while (m.find()) {

                String s1 = m.group(1);

                // 如果节点以/结尾,代表此节点不包含子节点

                if (s1.endsWith("/")) {

                    childrenDocs.add(m.group());

                    // 如果节点既不以/开头,也不以/结尾则表示需要查找对应的闭合节点

                else if (!s1.startsWith("/") && !s1.endsWith("/")) {

                    // 计算起始字符数

                    int start = m.end() - m.group().length();

                    // 如果捕获到未闭合节点则index++,如果捕获到闭合节点则index--

                    int index = 1;

                    while (m.find()) {

                        String s2 = m.group(1);

                        if (!s2.startsWith("/") && !s2.endsWith("/")) {

                            index++;

                        else if (s2.startsWith("/")) {

                            index--;

                        }

                        // 找到符合条件的闭合节点则循环终止

                        if (index == 0) {

                            break;

                        }

                    }

                    // 计算结束字符数

                    int end = m.end();

                    // 截取对应字符串

                    childrenDocs.add(inner.substring(start, end));

                }

            }

        }

        return childrenDocs;

    }

 

}

 

SimpleXmlParser.java

四、编写NodeBuilder类

根据过滤器和解析器获取Node节点各属性的值。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

import java.io.File;

import java.io.IOException;

import java.util.List;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

 

// 生成Node

public class NodeBuilder {

    private Node root = new Node();

    private XmlParser parser;

    private XmlFilter filter;

 

    // 提供合适的过滤器和解析器

    public NodeBuilder(XmlParser parser, XmlFilter filter) {

        this.parser = parser;

        this.filter = filter;

    }

 

    public Node getRoot(String... regex) {

        String str = null;

        if (regex.length == 0) {

            str = filter.filter();

        else {

            str = filter.filter(regex);

        }

        buildNodeTree(str, root);

        return root;

    }

 

    // 设置节点类型

    private void buildNodeTitle(String str, Node n) {

        Pattern p = Pattern.compile("<.*?>");

        Matcher m = p.matcher(str);

        if (m.find()) {

            String temp = m.group();

            String s = temp.substring(1, temp.length() - 1).split(" ")[0];

            if (s.endsWith("/")) {

                n.setTitle(s.substring(0, s.length() - 1));

            else {

                n.setTitle(s.split(" ")[0]);

            }

        }

    }

 

    // 设置节点属性集合

    private void buildNodeAttribute(String str, Node n) {

        Pattern p = Pattern.compile("<.*?>");

        Matcher m = p.matcher(str);

        if (m.find()) {

            String temp = m.group();

            String s = temp.substring(1, temp.length() - 1);

            // 匹配字符串

            p = Pattern.compile("(\\S*)=\"(.*?)\"");

            m = p.matcher(s);

            while (m.find()) {

                String key = m.group(1).trim();

                String value = m.group(2).trim();

                n.getAttribute().put(key, value);

            }

            // 匹配数字

            p = Pattern.compile("(\\S*)=(-?\\d+(\\.\\d+)?)");

            m = p.matcher(s);

            while (m.find()) {

                String key = m.group(1).trim();

                String value = m.group(2).trim();

                n.getAttribute().put(key, value);

            }

        }

    }

 

    // 设置节点内容,节点的内容是删除了所有子节点字符串以后剩下的部分

    private void buildNodeText(String str, Node n) {

        Pattern p = Pattern.compile("<.*?>(.*)</.*?>");

        Matcher m = p.matcher(str);

        List<String> childrenDocs = parser.parser(str);

        if (m.find()) {

            String temp = m.group(1);

            for (String s : childrenDocs) {

                temp = temp.replaceAll(s, "");

            }

            n.setText(temp.trim());

        }

    }

 

    // 通过递归生成完整节点树

    private void buildNodeTree(String str, Node n) {

        buildNodeTitle(str, n);

        buildNodeAttribute(str, n);

        buildNodeText(str, n);

        // 如果存在子节点则继续下面的操作

        if (!parser.parser(str).isEmpty()) {

            // 对每一个子节点都应该继续调用直到递归结束

            for (String temp : parser.parser(str)) {

                Node child = new Node();

                buildNodeTitle(temp, child);

                buildNodeAttribute(temp, child);

                buildNodeText(temp, child);

                n.getChildNode().add(child);

                buildNodeTree(temp, child);

            }

        }

    }

}

 

NodeBuilder.java

五、测试

编写xml测试文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

<package>

    <!-- 这里是注释1 -->

    package message before!

    <class id="exp1" path="www.sina.com"/>

    <class id="exp2">

        <class id="inner">

            class message inner.

        </class>

    </class>

    package message middle!

    <!-- 这里是注释2 -->

    <class id="exp3">

        <method id="md" name="setter" order=1>

            <!-- 这里是注释3 -->

            <!-- 这里是注释4 -->

            <para ref="String"/>

            <para ref="exp1">

                method message inner!

            </para>

        </method>

    </class>

    package message after!

</package>

 

测试文件

编写测试类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

import java.io.File;

import java.io.IOException;

 

public class Demo {

    public static void main(String[] args) {

        File f = new File("xxx");

        XmlFilter filter = null;

        try {

            filter = new SimpleXmlFilter(f);

        catch (IOException e) {

            e.printStackTrace();

        }

        XmlParser parser = new SimpleXmlParser();

        NodeBuilder builder = new NodeBuilder(parser, filter);

        Node node = builder.getRoot();

        System.out.println(node);

    }

}

 

Demo.java

输出

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<package>

package message before!package message middle!package message after!

<class path="www.sina.com" id="exp1"/>

<class id="exp2">

<class id="inner">

class message inner.

</class>

</class>

<class id="exp3">

<method name="setter" id="md" order="1">

<para ref="String"/>

<para ref="exp1">

method message inner!

</para>

</method>

</class>

</package>

 

System.out.println...


最新手记推荐

• 用composer安装thinkphp框架的步骤• 省市区接口说明• 用thinkphp,后台新增栏目• 管理员添加编辑删除• 管理员添加编辑删除

全部回复(0)我要回复

暂无评论~
  • 取消回复发送