abstract:這是我通過(guò)正則表達(dá)式實(shí)現(xiàn)的xml文件解析工具,有些XHTML文件中包含特殊符號(hào),暫時(shí)還無(wú)法正常使用。設(shè)計(jì)思路:常見(jiàn)的xml文件都是單根樹(shù)結(jié)構(gòu),工具的目的是通過(guò)遞歸的方式將整個(gè)文檔樹(shù)裝載進(jìn)一個(gè)Node對(duì)象。xml文檔樹(shù)上的每一個(gè)節(jié)點(diǎn)都能看做一個(gè)Node對(duì)象,它擁有title、attribute和text三個(gè)自身變量以及一個(gè)childrenNode集合用來(lái)存放子節(jié)點(diǎn),使用正則表達(dá)式完整裝載。一、編寫N
這是我通過(guò)正則表達(dá)式實(shí)現(xiàn)的xml文件解析工具,有些XHTML文件中包含特殊符號(hào),暫時(shí)還無(wú)法正常使用。
設(shè)計(jì)思路:常見(jiàn)的xml文件都是單根樹(shù)結(jié)構(gòu),工具的目的是通過(guò)遞歸的方式將整個(gè)文檔樹(shù)裝載進(jìn)一個(gè)Node對(duì)象。xml文檔樹(shù)上的每一個(gè)節(jié)點(diǎn)都能看做一個(gè)Node對(duì)象,它擁有title、attribute和text三個(gè)自身變量以及一個(gè)childrenNode集合用來(lái)存放子節(jié)點(diǎn),使用正則表達(dá)式完整裝載。
一、編寫Node類
Node對(duì)象是文檔解析的基礎(chǔ),最終可以通過(guò)對(duì)象的不同屬性實(shí)現(xiàn)對(duì)文檔信息的訪問(wèn)。
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 { // 可以對(duì)Node對(duì)象持久化保存 private static final long serialVersionUID = 1L; private int id; // 節(jié)點(diǎn)類型 private String title; // 節(jié)點(diǎn)內(nèi)容 private String text; // 節(jié)點(diǎn)屬性集合 private Map<String, String> attributes = new HashMap<String, String>(); // 子節(jié)點(diǎn)集合 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; } // 將屬性集合轉(zhuǎn)換成一條完整的字符串 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(); } // 輸出完整的節(jié)點(diǎn)字符串也用到了遞歸 @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
二、創(chuàng)建接口
把文檔的讀取和分析抽象成接口方便今后替換實(shí)現(xiàn)。
過(guò)濾器:讀取文檔的字符流并刪除注釋的部分。這些信息通常是提供給人閱讀的,程序分析直接忽略。
/* * 過(guò)濾器的作用是刪除xml文件中不重要的部分。 * 通常都是一些注釋性文字,不需要被機(jī)器解析。 */ public interface XmlFilter { String filter(); // 提供自定義正則表達(dá)式,識(shí)別符合過(guò)濾條件的字符串 String filter(String[] regex); } XmlFilter.java
解析器:將一個(gè)父節(jié)點(diǎn)解析成多條子節(jié)點(diǎn)的字符串。如果返回值為null,代表當(dāng)前節(jié)點(diǎn)下不存在可以繼續(xù)解析的對(duì)象。
import java.util.List; /* * 解析器可以對(duì)一段完整的父節(jié)點(diǎn)字符串提供解析服務(wù)。 * 將一條父節(jié)點(diǎn)的字符串解析成為多條子節(jié)點(diǎn)字符串 */ public interface XmlParser { // 解析一段父節(jié)點(diǎn),返回子節(jié)點(diǎn)字符串 List<String> parser(String str); } XmlParser.java
三、根據(jù)接口編寫實(shí)現(xiàn)類
回車、換行、制表符以及各種注釋部分的內(nèi)容都被刪除,簡(jiǎn)化字符輸出。
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; // 常用的過(guò)濾正則表達(dá)式 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
主要是通過(guò)正則表達(dá)式區(qū)分一個(gè)節(jié)點(diǎn)內(nèi)部的子節(jié)點(diǎn),考慮到節(jié)點(diǎn)的類型我將它們分為自閉合與非自閉合兩種類型。<title attributes .../>這樣的節(jié)點(diǎn)屬于自閉合類型,它們不包含子節(jié)點(diǎn)和text屬性,它們屬于文檔樹(shù)的葉子節(jié)點(diǎn)。<title attributes ...>text ...</title>這樣的節(jié)點(diǎn)屬于非自閉合類型,它們屬于文檔樹(shù)的分支節(jié)點(diǎn)。
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>(); // 捕獲根節(jié)點(diǎn)中間的文本 Pattern p = Pattern.compile("<.*?>(.*)</.*?>"); Matcher m = p.matcher(text); if (m.matches()) { String inner = m.group(1); // 匹配節(jié)點(diǎn)字符串 p = Pattern.compile("<(.*?)>"); m = p.matcher(inner); while (m.find()) { String s1 = m.group(1); // 如果節(jié)點(diǎn)以/結(jié)尾,代表此節(jié)點(diǎn)不包含子節(jié)點(diǎn) if (s1.endsWith("/")) { childrenDocs.add(m.group()); // 如果節(jié)點(diǎn)既不以/開(kāi)頭,也不以/結(jié)尾則表示需要查找對(duì)應(yīng)的閉合節(jié)點(diǎn) } else if (!s1.startsWith("/") && !s1.endsWith("/")) { // 計(jì)算起始字符數(shù) int start = m.end() - m.group().length(); // 如果捕獲到未閉合節(jié)點(diǎn)則index++,如果捕獲到閉合節(jié)點(diǎn)則index-- int index = 1; while (m.find()) { String s2 = m.group(1); if (!s2.startsWith("/") && !s2.endsWith("/")) { index++; } else if (s2.startsWith("/")) { index--; } // 找到符合條件的閉合節(jié)點(diǎn)則循環(huán)終止 if (index == 0) { break; } } // 計(jì)算結(jié)束字符數(shù) int end = m.end(); // 截取對(duì)應(yīng)字符串 childrenDocs.add(inner.substring(start, end)); } } } return childrenDocs; } } SimpleXmlParser.java
四、編寫NodeBuilder類
根據(jù)過(guò)濾器和解析器獲取Node節(jié)點(diǎn)各屬性的值。
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; // 提供合適的過(guò)濾器和解析器 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; } // 設(shè)置節(jié)點(diǎn)類型 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]); } } } // 設(shè)置節(jié)點(diǎn)屬性集合 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); } // 匹配數(shù)字 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); } } } // 設(shè)置節(jié)點(diǎn)內(nèi)容,節(jié)點(diǎn)的內(nèi)容是刪除了所有子節(jié)點(diǎn)字符串以后剩下的部分 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()); } } // 通過(guò)遞歸生成完整節(jié)點(diǎn)樹(shù) private void buildNodeTree(String str, Node n) { buildNodeTitle(str, n); buildNodeAttribute(str, n); buildNodeText(str, n); // 如果存在子節(jié)點(diǎn)則繼續(xù)下面的操作 if (!parser.parser(str).isEmpty()) { // 對(duì)每一個(gè)子節(jié)點(diǎn)都應(yīng)該繼續(xù)調(diào)用直到遞歸結(jié)束 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
五、測(cè)試
編寫xml測(cè)試文件
<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> 測(cè)試文件
編寫測(cè)試類
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
輸出
<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...