文章目录
资源文件
通过一个Class对象获取该类的资源,如Test.class.getResource(fileName)返回资源的URL或Test.class.getResourceAsStream(fileName)返回资源的输入流,fileName是相对于类所在的目录的相对路径,无论哪种系统,fileName各级目录间用/分割,如果fileName是以/开始的绝对路径,那么根目录是与包的定义方式一样的根目录,也就是classpath目录。
属性映射
属性映射Properties类是一种储存键值对的数据结构,它的键和值都是字符串,相对于Map,它很容易读写文件。
// 该方法间接继承自Map,所以Map的方法他都有。
public class Properties extends Hashtable<Object,Object> {
public Properties();
public Properties(Properties defaults); // defaults对应默认值,在当前属性映射里面找不到的时候会到default里面去找;
public synchronized Object setProperty/put(String key, String value); // 设置值;
public String getProperty(String key); // 如果没有对应值,则看defaults里面有对应默认值不,都没有返回null;
public Stream getProperty(String key, String default); // 在调用getProperty(key)返回null时返回default;
public synchronized void load(Reader reader/InputStream is); // 加载属性值;
public void store(Writer writer/OutputStream os, String comments); // comments是文件头的注释,存储时会自动加上注释符号#;
public synchronized void loadFromXML(InputStream in); // 从xml文件加载属性,xml文件必须为<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
public void storeToXML(OutputStream os, String comment); // 存储为xml格式
public Set<String> stringPropertyNames(); // 谁有key的集合;
}
Preferences存储
一般操作系统中都有一个树状的中心知识库,每一节点都对应一个存放键值对的表,这些表数据存储在本地机器上,如windows存储在注册表中,linux存储在一个隐藏文件中,我们一般不手动去更改这些信息,而是用api去更改他们。系统中有多个并行的树,其中分为系统树和用户树,系统树一个系统只一颗,而用户树每个操作系统的登录用户都只有一颗,我们用preference存放配置信息,区分系统树和用户树就能区分公用配置和各个登录用户的独特配置。我们一般让一个应用有一个单独的节点,所以习惯用该应用的包路径作为对应的节点路径。
public abstract class Preferences {
public static Preferences userRoot(); // 获取操作系统当前登录用户的用户树的根;
public static Preferences systemRoot(); // 获取操作系统系统树的根;
public static Preferences userNodeForPackage(Class c); // 获取相对于当前登录用户树根以c所在包相应路径为路径的节点,无节点就创建节点;
public static Preferences systemNodeForPackage(Class c); // 获取相对于系统树根以c所在包相应路径为路径的节点,无节点就创建节点;
public Preferences node(String path); // 获取节点,path以/为分隔符,如果以/开头则为相对于调用该方法的节点的根(用户树或系统树根)开始,否则相对于调用节点开始;
public abstract void removeNode(); // 移除当前noed;
public void put(String key, String value); // 直接就进行了持久化保存;
public void putXxx(String key, xxx value); // xxx为基本类型或byte[], byte[]对应的Xxx为ByteArray;
public String get(String key, String default); // 获取当前节点相对键值的值,如果值不存在或类型不匹配或系统不支持中心知识库就返回default;
public xxx getXxx(String key, xxx default); //
public String remove(String key); // 从当前节点删除key对应的键值对;
public String[] keys(); // 该节点所有的键值;
public void clear(); // 清除节点的键值;
public void exportSubtree(OutputStream os); // 导出该节点及其子节点相关数据以xml格式保存;
public void exportNode(OutpurStream os); // 导出该节点相关数据并以xml格式保存;
public void importPreference(InputStream is); // 导入键值对,导入导出可以用于应用迁移到另一个平台;
public String[] childrenNames(); // 当前节点的直接子节点名;
public Preferences parent(); // 获取父节点,如果当前节点已经是root节点则返回null;
public String name(); // 返回节点名;
public abstract String absolutePath(); // 返回相对于root的绝对路径;
public boolean nodeExists(String pathName); // pathName为路径的节点是否存在;
public abstract boolean isUserNode(); //
public abstract void addPreferenceChangeListener(PreferenceChangeListener pcl);
public abstract void removePreferenceChangeListener(PreferenceChangeListener pcl);
public abstract void addNodeChangeListener(NodeChangeListener ncl);
public abstract void removeNodeChangeListener(NodeChangeListener ncl);
}
XML解析
Java解析XML的方式分为基于对象与基于事件两种方式,其常用的框架主要有四种,DOM解析、SAX解析、StAX解析、JDOM解析与DOM4J解析,其中DOM与SAX已包含在JRE内置包中,而JDOM与DOM4J需要引入第三方包,如果不是特殊需求,推荐使用DOM4J,其性能最好。
基于事件的框架,其思路为,一面解析文件,一面处理业务,每当有事件发生(如开始读取文档、结束读取文档、读到某个标签、结束某个标签等),就回调用户实现的处理器进行业务处理。基于对象的框架凌驾于基于事件的框架之上,让基于事件的框架回调的处理器不进行业务处理,而将事件记录成一个对象,最后由用户实现对记录的对象进行业务处理,这就形成了基于对象的框架。
XPath语法规则
XPath是一门在XML文档中查找信息的语言,它可用来在XML文档中对元素和属性进行遍历。XPath表达式的组成分为路径分隔符和步,型如…/step1//step2/setp3。另外可以用 | 连接两个xpath表达式取两者的并集。实际上XPath表达式可以表示数值、字符串、boolean以及节点集,在DOM做XPath解析的时候可以把一个XPath表达式解析成以上几种类型。
路径分隔符有 / 和 //,/ 连接的两个step具有直接的父子关系,而 // 连接的两个step只需要具有祖先后代关系。路径可分为绝对路径与相对路径,绝对路径以 / 或者 // 开头,相对路径以步开头,以 / 开头接的step必须是根下的第一级step才能匹配,而以 // 开头接的step是根下的任一级step都可以匹配。
步的格式为轴名称::节点测试[谓语],有 . 与 … 可以认为是特殊的步,表示当前节点与父节点。
轴名称有self(当前节点)、ancestor(祖先节点)、descendant(后代节点)、ancestor-or-self、descendant-or-self、parent(父节点)、child(子节点)、preceding(文档中位于当前节点之前(开始标签之前)的完整节点(开始标签与结束标签都在当前节点之前的节点))、preceding-sibling(preceding中与当前节点是兄弟关系的节点)、following(文档中位于当前节点之后(结束标签之后)的完整节点(开始标签和结束标签都在当前节点之后的节点))、following-sibling(following中与当前节点是兄弟关系的节点)、attribute(当前节点的所有属性子节点)、namespace(本座也没明白这个轴的意义),attribute与namespace以外的轴不会包含attribute节点,当轴名称为child时,可以省略child::,当为attribute时可以用@代替attribute::。
节点测试对轴解析出的节点进行过滤,最常用的节点测试是节点名,用于根据节点名过滤,另外有 *, 当轴为attribute时表示筛选出属性节点,当轴为其他时表示筛选出element节点,text()表示筛选出文本节点,processing-instruction()表示筛选出指令节点,node()表示筛选出任意类型的节点,comment()表示筛选出注释节点。
谓语负责对节点测试得到的节点集依次遍历进行过滤(本次遍历我们暂时叫着上下文,遍历到某个节点就把这个节点暂时叫着当前节点),谓语中可以有常量、运算符、函数以及子xpath表达式(相对路径都是相对于当前节点的,绝对路径依然是相对于文档节点,xpath的值是一个nodeset类型)。当谓语的值为数值类型n时,表示index为n的当前节点会被选中(等同于position() = n),当谓语的值不是数值类型时,用boolean函数作用于谓语的值为真的当前节点会被选中
常量有数值常量与字符串常量,数字常量就为数字字面值,字符串常量用双引号或者单引号包裹,因为不用字符串包裹且不是函数(不接括号)的字符串表示xpath表达式,所以没有boolean类型常量(true会被认为是xpath的步)。数字常量有些特殊的值,非数值NaN(不能转换为数字的字符串非要转换为字符串得到,转换为字符串为’NaN’)、正无穷大Indinity(正数除以0得到,转换为字符串为’Infinity’)、负无穷大-Infinity(负数除以0得到,转换为字符串为’-Infinity’)。NaN参与任何数值计算都得到NaN,任意两个NaN都不相等,如 ‘a’ + 1 != ‘a’ + 1返回真,判断一个数值是否为NaN的方法是将数值用string函数处理后看是否为字符串’NaN’;任意两个正无穷都相等,任意两个负无穷也都相等,那么判断一个数值是否为正无穷可以与 1 div 0比较,如 3 div 0 = 1 div 0 返回真,也可以用string函数处理后的值是否为’Infinity’来判断是否为正无穷,负无穷同理。
xpath中的运算符有数值运算(+、-、*、div、mod)、比较运算符(=、!=、>、<、>=、<=)和逻辑运算符(or、and),可以用小括号来定义运算优先级。对于逻辑运算,先对参与运算的值调用boolean函数进行处理后再进行运算;对于数值运算以及比较运算中的 >、<、>=、<=,先对参与运算的值调用number函数进行处理后再进行运算;对于比较运算中 = 与 !=,如果有boolean类型参与运算,那么把非boolean类型操作数调用boolean函数(所以 true() = 2的结果是真)后参与运算,否则如果有number类型参与运算,那么将非number类型操作数调用number函数后参与运算。在所有比较运算中(=、!=、>、<、>=、<=),如果有节点集参与运算,那么就是遍历节点集中的每一个节点的值来运算,只要有一次运算结果为真,那么本次比较的结果就为真。如果两个结果集进行比较,那么可能比较符为任何比较符结果都为真。
xpath中常用在谓语中的函数(? 表示该参数可以有或者没有(没有默认为当前节点),* 表示该参数可以没有或者有多个,nodeset表示参数只能是节点集,any表示参数可以是任何类型,string / number / boolean表示参数为任何类型并且会首先调用string(any) / number(any) / boolean(any)将参数处理为string / number / boolean类型):
- 节点集相关
- number postion() 返回当前节点在当前上下文中的位置号数,位置号数从1开始
- number last() 返回最后一个节点在当前上下文中的位置号数
- number count(nodeset) 返回节点集的节点数量
- nodeset id(any) 根据在DTD中声明为ID类型的标识符选择元素,返回一个节点集
- string local-name(nodeset?) 返回节点集中第一个节点的不带前缀的节点名
- string namespace-uri(nodeset?) 返回节点集中第一个节点的命名空间uri
- string name(nodeset?) 返回节点集中第一个节点的prefix:local-name
- 字符串相关
- string string(any?) boolean返回’true’或’false’,数字返回对应的字符串,字符串原值返回,nodeset返回第一个节点的值
- string concat(string, string, string*) 连接字符串
- boolean start-with(string, string) 第一个字符串参数是否是以第二个字符串参数开头
- boolean contains(string, string) 第二个字符串参数是否是第一个字符串参数的子串
- string substring-after/before(string, string) 第二个参数在第一个参数中第一次出现的位置前/后的子串
- string substring(string, number, number?) 子串,第三个参数默认为第一个参数的长度
- number string-length(string?) 返回string的长度
- string normalize-space(string?) 清除string头尾的空白字符并且把中间连续的空白字符替换为一个再返回
- translate(string1, string2, string3) 将string1中的string2替换为string4(当string3的长度大于string2的长度时,string4取string3的前string-leng(string2)个字符,否则string4就取string3),如translate(‘abab’, ‘ab’, ‘cde’) = ‘cdcd’,translate(‘abab’, ‘ab’, ‘c’) = ‘cc’,translate(‘abab’, ‘ab’, ‘cd’) = ‘cdcd’
- boolean相关
- boolean true() 真
- boolean false() 假
- boolean boolean(any) 数字非0为真,集合非空为真,字符串非空串为真
- boolean not(boolean) 取相反的值
- boolean lang(string) 检查当前节点的xml:lang属性是否为string的值
- 数字相关
- number number(any?) 数字返回其原值,布尔值true返回1,false返回0;节点集首先转换成字符串,字符串转换成对应的数字,默认为当前节点
- number sum(nodeset) 对节点集nodeset中的所有节点应用number()函数后返回和
- number round(number) 返回四舍五入的值
- number ceiling(number) 返回不小于数字number的最小整数
- number floor(number) 返回不大于数字number的最大整数
解析路径从前往后依次解析,每一级step的解析都是对上一级step解析得到的所有节点依次 独立 处理后合并。
/**
代码模拟步的处理过程
@param:parentStepNodes 上一step筛选得到的节点集
@return: 本次step得到的节点集
*/
Set<Node> selectNodes(Set<Node> parentStepNodes) {
Set<Node> returnNodes = new HashSet<>();
for (Node dealNode: parentStepNodes) {
Set<Node> thisStepNodes = axis(dealNode); // 根据dealNode与轴得到节点集
thisStepNodes = nodeTest(thisStepNodes); // 保留符合测试节点
for (Node currentNode: thisStepNodes) {
Object predicateValue = redicate(currentNode); // 谓语运算值
if (predicateValue instanceof Integer) { // 当谓语值为整数时
if (thisStepNodes.indexOf(currentNode) == predicateValue) { 如果当前节点在测试节点中的位置为谓语值时
thisStepNodes.remove(currentNode);
}
} else if (predicateValue instanceof String) {
if (predicateValue == '') {
thisStepNodes.remove(currentNode);
}
} else if (predicateValue instanceof Boolean) {
if (!predicateValue) {
thisStepNodes.remove(currentNode);
}
} else if (predicateValue instanceof Set) {
if (predicateValue.size() == 0) {
thisStepNodes.remove(currentNode);
}
} else {
...
}
}
returnNodes.addAll(thisStepNodes);
}
return returnNodes;
}
实例分析
<a id="a1">
<b id="b1">
<c id="c1">
<d>
</d>
</c>
<c id="c2"> 35 </c>
</b>
<b id="b2" battr1="35">
<c id="c3">20</c>
</b>
</a>
xpath | 解析结果(节点的id) | 解析分析 |
---|---|---|
/a/b/c[1] | c1、c3 | /a/b得到两个节点b1、b2,解析b1的子节点并且节点名为c的第一个得到c1(c2被丢弃),同样解析b2得到c3,所以最后结果是c1、c3 |
/a/b[1]/c | c1 、c2 | /a/b[1]得到一个节点b1(b2被丢弃),解析b1的子节点并且节点名为c的所有节点得到c1、c2 |
/a/b[1]/c[1] | c1 | /a/b[1]得到一个节点b1(b2被丢弃),解析b1的子节点并且节点名为c的第一个得到c1(c2被丢弃) |
/a/b[1]/c[1] | c1 | /a/b[1]得到一个节点b1(b2被丢弃),解析b1的子节点并且节点名为c的第一个得到c1(c2被丢弃) |
/a/b[1]/…/b | b1、b2 | /a/b[1]得到一个节点b1(b2被丢弃),解析b1的父节点得到a1,解析a1的子节点中名为b的节点b1、b2 |
//b/c | c1 、c2、c3 | //b获取所有名称为b的节点,得到b1,b2,解析b1的子节点并且节点名为c的节点得到c1、c2,同样解析b2得到c3,所以最后结果是c1、c2、c3 |
/a//c | c1、c2、c3 | |
/a/b/text() [2 - 1] | 两个文本节点 | /a/b得到b1与b2,解析b1的子节点中为文本节点的节点中的第一个,同理得到b2的第一个文本节点,故最后得到的是b1下的第一个文本节点和b2下的第一个文本节点 |
/a/b/c[self::*>30] | c2 | /a/b找打b1与b2,解析b1的子节点中节点名为c并且节点的值大于30的节点得到c2,同理得到对b2的解析为空,两者并集为c2 |
/a/b[c>30] | b1 | /a得到a1,a1下所有节点名为b且拥有子节点c的值大于30的节点 |
/a/b[c>30]/c | c1、c2 | /a/b[c>30]得到b1,解析b1的子节点中名为c的元素 |
/a/b/c[self::*>30] | /a/b[c>30] | c2、b1 | /a/b/c[self::*>30]得到c2,/a/b[c>30]得到b1,两者并集为c2、b1 |
/a/b[@battr1>20] | b1 | /a得到a1,a1的子节点中名为b且具有battr1属性且属性值大于20的节点 |
/a/b[@battr1] | b1 | /a得到a1,a1的子节点中名为b且具有battr1属性的节点 |
/a/b/c/*[namespace-uri() = ‘http://www.wanglang.com’ and local-name() = ‘d’] | d1 | |
/a/b/c[/a/b/c/*[/a/b[c>30]] | c1、c2、c3 | 这是嵌套的xpath, /a/b[c>30]的结果是b1,所以/a/b/c[这里面是一个元素个数为1(b1)的节点集,节点集非空判断为真] |
DOM(Document Object Model)解析方式
DOM解析方式会读取整个XML文件并将其解析为一个Document对象(实现了org.w3c.dom.Document接口),通过对这个Document对象的操作,来实现对XML文档数据的操作。DOM强制使用树模型来访问XML文档中的信息,是解析XML的官方标准(由W3C定义)。org.w3c.dom.Document是个接口,Java提供了工厂方法来获得Document实例。DOM解析方式对XML的解析其实就是对Document的分析。
创建一个Document对象,首先需要通过DocumentBuilderFactory的静态方法newInstance()获取一个DocumentBuilderFactory对象,然后通过该工厂的newDocumentBuilder方法获取一个Document的解析器DocumentBuilder对象,最后通过解析器DocumentBuilder的parse方法将一个xml文件解析为一个Document对象,或newDocument方法构建一个空的Document对象。如果在解析XML时有特殊需求,如忽略逃逸字符等,可以通过设置工厂的某些属性来实现。
将一个Document保存为xml文件,首先需要通过TransformerFactory的静态方法newInstance()获取一个TransformerFactory对象,然后通过该工厂的newTransformer()方法获取一个转换器Transformer对象,最后通过转换器对象的transform方法将一个source转换为result(document保存到流中时,Source为DomSource,Result为StreamSource,其实该方法也可以从流中读取为document,此时Source为StreamSource,Result为DomResult,DomResult的getNode就是document节点)。
// 解析器工厂
public abstract class DocumentBuilderFactory {
public static DocumentBuilderFactory newInstance();
public abstract DocumentBuilder newDocumentBuilder();
... 另外有一系列函数,用来为创建的解析器的解析细节设置控制参数,比如忽略注释,是否验证DTD等,
默认是不支持命名空间的,所以建议创建DocumentBuilderFactory对象后先调用其setNamespaceAware(true)方法来支持命名空间;
}
// 解析器
public abstract class DocumentBuilder {
public void reset(); // 将此 DocumentBuilder 重置为其原始配置
public Document parse(InputStream is / String uri / File f); // 解析出一个新的Document对象
public abstract Document newDocument(); // 获取一个新的Document对象
public abstract void setEntityResolver(EntityResolver er); // 自定义处理实体
public abstract void setErrorHandler(ErrorHandler eh);
... 另外有一系列控制解析相关的函数比较少用(都是读函数,没有写函数,这些函数的控制参数在工厂创建解析器时提供,也由解析器工厂配置)
}
DOM模式基于树模型,既然树模型就会有节点,这些节点被分为了多个类型。整个文档是一个文档节点,每个XML标签是一个元素节点、包含在XML元素中的文本是文本节点、每一个XML 属性是一个属性节点、注释属于注释节点等。Java中Node有子类Document、DocumentType、Element、Attr、Text、CDATASection、Comment、EntityReference、Entity、ProcessingInstruction、DocumentFragment、Notation,其中有些节点类型可以有子节点,有些没有子节点。
Document是整个文档,对于xml文件里面第一层的所有节点都是其子节点。Attr与Text都是对应Element的子节点。
// 一个节点
public interface Node {
public String getNodeName();
public String getNodeValue();
public short getNodeType();
public NamedNodeMap getAttributes();
public boolean hasChildNodes();
public String getNamespaceURI();
public boolean hasAttributes();
public String getTextContent();
public boolean isSameNode(Node other);
public boolean isDefaultNamespace(String namespaceURI);
...另外还有一系列获取父节点、子节点、兄弟节点的方法
...另外还有一系列的修改信息、增加删除子节点等的方法,用得比较少
}
// 就是一个Node集合
public interface NodeList {
public Node item(int index);
public int getLength();
}
// 在解析时一般只会用到读方法
public interface Document extends Node {
public DocumentType getDoctype(); // 获取doctype节点
public DOMImplementation getImplementation();
public Element getDocumentElement(); // 一个XML只能有一个根元素,所以一个document也只会有一个根元素,该方法用来获取根元素
public NodeList getElementsByTagName(String tagname); // 获取为标签为tagname的element节点
public NodeList getElementsByTagNameNS(String namespaceURI, String localName);
public Element getElementById(String elementId);
public String getInputEncoding();
public String getXmlEncoding();
public boolean getXmlStandalone();
public String getXmlVersion();
public boolean getStrictErrorChecking();
public String getDocumentURI();
public DOMConfiguration getDomConfig();
public void normalizeDocument();
... 还有一系列非读方法比较少用
}
// 对应一个标签
public interface Element extends Node {
public String getTagName(); // 标签名字
public boolean hasAttribute(String name);
public String getAttribute(String name); // 获取属性值,属性节点的值
public String getAttributeNS(String namespaceURI, String localName);
public boolean hasAttributeNS(String namespaceURI, String localName);
public Attr getAttributeNode(String name); // 获取属性节点
public Attr getAttributeNodeNS(String namespaceURI, String localName);
public NodeList getElementsByTagName(String name); // 获取该element内部标签为name的element节点
public NodeList getElementsByTagNameNS(String namespaceURI, String localName);
public TypeInfo getSchemaTypeInfo();
... 还有一系列非读方法比较少用
}
DOM支持XPath操作,首先由XPathFactory的静态方法newInstance创建工厂对象,然后调用工厂对象的newXPath方法获取一个XPath对象,最后由XPath对象的evaluate方法来解析XPath表达式(也可以由XPath对象的compile方法创建一个XPathExpression对象,然后由XPathExpression对象的evaluate方法来解析)。DOM工厂创建DOM时默认是关闭了命名空间的解析功能的,所以在使用时需要打开其命名空间解析功能(在创建DocumentBuilderFactory对象后调用其setNamespaceAware(true)方法)。
public abstract class XPathFactory {
public static XPathFactory newInstance(); // 创建工厂实例
public abstract XPath newXPath(); // 创建XPath实例
public abstract void setFeature(String name, boolean value);
public abstract boolean getFeature(String name);
}
public interface XPath {
public void reset(); // 还原为初试配置
public XPathExpression compile(String expression); // 得到一个XPath表达式对象
public Object evaluate(String expression, Object item, QName returnType); // 同compile(expression).evaluate(item, returnType)
public String evaluate(String expression, Object item); // 同compile(expression).evaluate(item)
public Object evaluate(String expression, InputSource source, QName returnType); // 同compile(expression).evaluate(source, returnType)
public String evaluate(String expression, InputSource source); // 同compile(expression).evaluate(source)
}
public interface XPathExpression {
// 当returnType取XPathConstants.NODESET时返回NodeList
// 当returnType取XPathConstants.NODE时返回NodeList的第一个Node
// 当returnType取XPathConstants.BOOLEAN时返回Boolean
// 当returnType取XPathConstants.STRING时返回String
// 当returnType取XPathConstants.NUMBER时返回Double
// 可以根据returnType强转返回值类型
public Object evaluate(Object item, QName returnType); // item为上下文,相对路径就相对于该上下文,如取document或者任意一个element
public String evaluate(Object item);
public Object evaluate(InputSource source, QName returnType); // 通过source构建上下文
public String evaluate(InputSource source);
}
SAX(Simple APIs for XML)解析方式
SAX基于事件的解析,解析器在一次读取XML文件中根据读取的数据产生相应的事件,由应用程序实现相应的事件处理逻辑。
SAX解析首先需要通过解析器工厂SAXParserFactory的newInstance方法获取一个解析工厂实例SAXParserFactory,然后通过该解析器工厂实例的newSAXParser方法创建解析器SAXParser,最后通过解析器实例SAXParser的parse方法解析XML并在解析过程中回调用户定义的处理器进行处理。SAX解析的关键就是编写处理器(处理器继承自DefaultHandler或者HandlerBase)。
javax.xml.parsers
public abstract class SAXParserFactory {
public static SAXParserFactory newInstance();
public static SAXParserFactory newInstance(String factoryClassName, ClassLoader classLoader);
public abstract SAXParser newSAXParser();
... 另外有一系列函数,用来为创建的解析器的解析细节设置控制参数
}
public abstract class SAXParser {
public void reset(); // 将解析器复位到最初状态
public void parse(InputStream is / String uri / File file / InputSource is, HandlerBase hb / DefaultHandler dh);
... 另外有一系列控制解析相关的函数比较少用
}
// 事件处理器
public class DefaultHandler implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler {
// ContentHandler
public void startDocument (); // 开始解析文件时触发
public void endDocument (); // 解析文件结束时触发
public void startElement (String uri, String localName, String qName, Attributes attributes); // 开始一个标签时触发
// uri代表命名空间的标识符,当元素没有命名空间或者解析器的命名空间支持特性没有打开时是空串,如http://www.namespace.com;
// localName代表没有前缀的本地名,当支持命名空间特性没有打开时是空串,如tag-name;
// qName代表带有前缀的限定名,当限定名不能获取时是空串,如jd:tagname;
// attributes代表该标签的所有属性,可以对其各个属性进行分析
public void endElement (String uri, String localName, String qName);
public void characters (char[] ch, int start, int length); // 标签内容事件的触发,从ch的start位置的length长度的字符是有效的,其他位置的字符看做是无效的。
// 如果一个标签的内容过长,可能内容会分段连续多次触发该事件,可以用一个全局变量记录内容,每一次把新的有效内容append到变量中,最后在结束标签中处理该变量,处理完将该变量设置为空字符串。
public void processingInstruction (String target, String data); // 处理形如<?xml-stylesheet type="type" href="uri" ?>的处理指令
public void startPrefixMapping (String prefix, String uri); // 接收命名空间映射的开始的通知
public void endPrefixMapping (String prefix); // 接收命名空间映射的结束的通知
// ErrorHandler
public void warning (SAXParseException e); // 警示那些不是由 XML 规范定义的错误,但是它可能值得注意
public void error (SAXParseException e); // 警示那些由 XML 规范定义的错误
public void fatalError (SAXParseException e); // 警示那些由 XML 规范定义的致命错误
// DTDHandler
public void notationDecl (String name, String publicId, String systemId); // 通知应用程序已经声明了一个标记
public abstract void unparsedEntityDecl (String name, String publicId, String systemId, String notationName); // 通知应用程序已经发现了一个未经过语法分析的实体声明
// EntityResolver
InputSource resolveEntity(String publicId, String systemId);
}
SAX2解析是SAX的新版本,它首先需要通过解析器工厂XMLReaderFactory的静态方法createXMLReader创建一个XMLReader实例,然后设置XMLReader需要处理的各种处理器,最后调用XMLReader实例的parse方法。
final public class XMLReaderFactory {
public static XMLReader createXMLReader ();
}
public interface XMLReader {
public void parse (InputSource input); // 根据本对象的配置开始解析文件
// 以下设置各类处理器,也可以把各类处理器都设置为同一个DefaultHandler的子类
public void setDTDHandler (DTDHandler handler);
public DTDHandler getDTDHandler ();
public void setContentHandler (ContentHandler handler);
public ContentHandler getContentHandler ();
public void setErrorHandler (ErrorHandler handler);
public ErrorHandler getErrorHandler ();
public void setEntityResolver(EntityResolver resolver);
// 设置参数控制解析器的工作细节,各种控制对应的name为什么,自行百度
public boolean getFeature (String name);
public void setFeature (String name, boolean value);
public Object getProperty (String name);
public void setProperty (String name, Object value);
}
SAX也可以生成XML文件,首先通过(SAXTransformerFactory)SAXTransformerFactory.newInstance()得到一个SAXTransformerFactory工厂实例,然后通过工厂的newTransformerHandler()方法获取一个处理器TransformerHandler,通过处理器setResult来设置保存位置,还可以通过处理器的getTransformer来获取转换器并对转换器进行一些配置(如设置输出编码格式等),最后调用处理器的函数进行处理(从startDocument函数开始,到endDocument函数结束,中间可以通过startElement与endElement来创建节点等操作)。
StAX(Streaming API for XML)解析方式
StAX是Java6之后内置的XML解析模型,与SAX类似是基于流模型的,但SAX是基于推模式,而StAX是基于拉模式的。StAX解析有两组框架,分别为基于指针和基于迭代器的方式。
// 事件类型定义
public interface XMLStreamConstants {
public static final int START_ELEMENT=1; // 指示事件是一个开始元素
public static final int END_ELEMENT=2; // 指示事件是一个结束元素
public static final int PROCESSING_INSTRUCTION=3; // 指示事件是一条处理指令
public static final int CHARACTERS=4; // 指示事件是一些字符
public static final int COMMENT=5; // 指示事件是一个注释
public static final int SPACE=6; // 字符是空格
public static final int START_DOCUMENT=7; // 指示事件是一个开始文档
public static final int END_DOCUMENT=8; // 指示事件是一个结束文档
public static final int ENTITY_REFERENCE=9; // 指示事件是一个实体引用
public static final int ATTRIBUTE=10; // 指示事件是一个属性
public static final int DTD=11; // 指示事件是一个 DTD
public static final int CDATA=12; // 指示事件是一个 CDATA 节
public static final int NAMESPACE=13; // 指示事件是一个名称空间声明
public static final int NOTATION_DECLARATION=14; // 指示一个 Notation
public static final int ENTITY_DECLARATION=15; // 指示一个 Entity Declaration
}
基于指针的方式首先通过XMLInputFactory的静态方法newFactory或newInstance获取一个XMLInputFactory对象,然后通过XMLInputFactory的createXMLStreamReader方法创建一个XMLStreamReader对象,最后对XMLStreamReader对象进行解析(该对象的大部分方法就是相对于当前事件的解析,其next方法将当前事件设置为下一个事件)。
基于指针的方式写入xml文件,首先通过XMLOutputFactory的静态方法newFactory或newInstance获取一个XMLOutputFactory对象,在通过XMLOutputFactory对象createXMLEventWriter方法创建一个XMLStreamWriter对象,然后可以对XMLStreamWriter对象做基本的设置,最后写入各种事件。
public abstract class XMLInputFactory {
public static XMLInputFactory newInstance/newFactory();
public abstract XMLStreamReader createXMLStreamReader(Reader reader / Source source / InputStream stream); // 创建一个基于指针的reader
public abstract XMLStreamReader createXMLStreamReader(InputStream stream, String encoding);
public abstract XMLEventReader createXMLEventReader(Reader reader / Source source / InputStream stream / XMLStreamReader reader); // 创建一个基于迭代器的reader
public abstract XMLEventReader createXMLEventReader(InputStream stream, String encoding);
public abstract void setProperty(java.lang.String name, Object value);
public abstract Object getProperty(java.lang.String name);
public abstract boolean isPropertySupported(String name);
}
public interface XMLStreamReader extends XMLStreamConstants {
public void close(); // 释放与此 Reader 关联的所有资源,此方法不会关闭底层输入源。
// 指针相关
public boolean hasNext();
public int next(); // 当前指针指向下一个事件并返回事件类型,事件类型定义在XMLStreamConstants中
public int nextTag(); // 在到达 START_ELEMENT 或 END_ELEMENT 之前,跳过所有空格、COMMENT 或 PROCESSING_INSTRUCTION,期间出现其他事件会抛出 异常
public int getEventType(); // 返回事件类型
public void require(int type, String namespaceURI, String localName); // 测试当前事件是否为type事件,并且名字空间为namespaceURI,标签名为localName,当namespaceURI和localName为null时表示该位置不检查
public boolean isStartElement(); // 判断事件是否为StartElemnt事件
public boolean isEndElement();
public boolean isCharacters();
public boolean isWhiteSpace();
public boolean hasName(); // 为START_ELEMENT 或 END_ELEMENT返回true,否则返回false
public String getElementText(); // 读取element的文本
public String getAttributeValue(String namespaceURI, String localName);
public int getAttributeCount();
public QName getAttributeName(int index);
public String getAttributeNamespace(int index);
public String getAttributeLocalName(int index);
public String getAttributePrefix(int index);
public String getAttributeType(int index);
public String getAttributeValue(int index);
public boolean isAttributeSpecified(int index);
public int getNamespaceCount();
public String getNamespacePrefix(int index);
public String getNamespaceURI(int index);
public NamespaceContext getNamespaceContext();
public QName getName();
public String getLocalName();
public String getNamespaceURI();
public String getPrefix();
public boolean hasText();
public String getText();
public char[] getTextCharacters();
public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length);
public int getTextStart();
public int getTextLength();
public String getPITarget();
public String getPIData();
public String getEncoding(); // 读取文件的reader的编码
public String getVersion();
public boolean standaloneSet(); // 返回xml文件是否设置了standalone
public boolean isStandalone(); // 返回设置的standalone的值
public String getCharacterEncodingScheme(); // xml文件中设置的编码
public Object getProperty(java.lang.String name);
}
基于迭代器的方式首先也是通过XMLInputFactory的静态方法newFactory或newInstance获取一个XMLInputFactory对象,然后通过XMLInputFactory的createXMLEventReader方法创建一个XMLEventReader对象,XMLEventReader对象就是一个迭代器,每一次调用XMLEventReader对象的nextEvent方法都会返回下一个XMLEvent对象,最后对XMLEvent进行解析。
基于迭代器的方式保存xml文件,直接调用XMLEvent的writeAsEncodedUnicode将当前事件写入writer。
public interface XMLEventReader extends Iterator {
public boolean hasNext();
public XMLEvent nextEvent(); // 获取下一个事件
public XMLEvent nextTag(); // 到下一个START_ELEMENT 或 END_ELEMENT事件,中途遇到无意义的空白事件直接跳过,遇到其他事件会抛出异常
public XMLEvent peek();
public String getElementText(); // 读取纯文本元素的内容
public Object getProperty(String name);
public void close(); // 释放资源(不会释放底层输入源,需要手动释放)
}
// 对于每一个类型的事件都有一个事件类实现了该接口,实际事件有:
// Attribute, Characters, Comment, DTD, EndDocument, EndElement, EntityDeclaration, EntityReference,
// Namespace, NotationDeclaration, ProcessingInstruction, StartDocument, StartElement
public interface XMLEvent extends javax.xml.stream.XMLStreamConstants {
public int getEventType(); // 事件类型定义在XMLStreamConstants中
public boolean isStartElement(); // 若返回ture,此时的XMLEvent可以强转为StartElement
public boolean isAttribute();
public boolean isNamespace();
public boolean isEndElement();
public boolean isEntityReference();
public boolean isProcessingInstruction();
public boolean isCharacters();
public boolean isStartDocument();
public boolean isEndDocument();
public StartElement asStartElement();
public EndElement asEndElement();
public Characters asCharacters();
public QName getSchemaType();
public void writeAsEncodedUnicode(Writer writer); // 写出到流
}
JDOM(Java Document Object Model)解析方式
JDOM也是基于对象的解析方式,它不是JRE内置的包,需要引入JDOM包到项目。在JDOM中主要分为几种类,分别放在几个不同的包中:
- org.jdom 包含最后解析出来的对象,主要包含了容器与节点
- org.jdom.filter 包含对各种节点列表进行过滤的过滤器基类
- org.jdom.input 构件JDOM的各类对象(主要是节点与容器),来源于XML文件或DOM对象,实现DOM对象转JDOM对象以及解析XML得到Document
- org.jdom.output 将JDOM的各类对象输出,如保存到XML文件或转换为DOM对象
- org.jdom.xpath 包含了对xml文档xpath操作的类
JDOM的树型结构中,有两个基类Content与Parent,Parent表示可以作为容器,Content表示一个节点(节点必然挂在一个容器下),Parent子类有Document与Element,Content子类有Comment、Element、EntityRef、Text、ProcessingInstruction、CDATA、DocType。Document是容器不是节点,它下面可以挂载节点但自身并不挂载到节点下,Element既是容器又是节点,它下面可以挂载节点,也可以挂载到其他容器下。
解析XML首先new一个SAXBuilder 对象,然后调用该SAXBuilder对象的build方法创建一个org.jdom2.Document,最后对该Document进行分析。
public class SAXBuilder implements SAXEngine {
public SAXBuilder();
public Document build(InputStream in / File file / URL url / InputSource in / Reader characterStream));
public void setFeature(String name, boolean value);
public void setProperty(String name, Object value);
public SAXHandlerFactory getSAXHandlerFactory() ;
public void setSAXHandlerFactory(SAXHandlerFactory factory);
public ErrorHandler getErrorHandler();
public void setErrorHandler(ErrorHandler errorHandler);
public DTDHandler getDTDHandler();
public void setDTDHandler(DTDHandler dtdHandler);
}
// 代表一个节点,只有这样的节点可以作为子节点
public abstract class Content {
public final CType getCType(); // 节点类型
public Content detach(); // 从父节点中去掉该节点
public Parent getParent();
final public Element getParentElement(); // 如果有父节点且是父节点是Element类型,那么返回父节点,否则返回null
protected Content setParent(Parent parent); // 设置父节点
public Document getDocument(); // 获取跟容器
public abstract String getValue();
public Content clone(); // 深克隆节点并将克隆的节点detach()
}
// 代表一个容器,只有容器可以作为父节点
public interface Parent extends Cloneable, NamespaceAware, Serializable {
Document getDocument();
Parent getParent();
List<Content> cloneContent(); // 深克隆所有子节点并将克隆的子节点detach()
Object clone(); // 深克隆当前节点及其子节点
int getContentSize(); // 子节点个数
int indexOf(Content child); // 某个节点是其第几个子节点或不是其子节点时返回-1
Content getContent(int index); // 获取子节点
List<Content> getContent(); // 获取所有子节点
<E extends Content> List<E> getContent(Filter<E> filter); // 获取过滤后的子节点
IteratorIterable<Content> getDescendants(); // 获取所有后代节点
<E extends Content> IteratorIterable<E> getDescendants(Filter<E> filter); // 获取过滤后的后代节点
List<Content> removeContent(); // 移除所有子节点
<E extends Content> List<E> removeContent(Filter<E> filter); // 移除符合条件的子节点
boolean removeContent(Content child);
Content removeContent(int index);
public Parent addContent(Content child);
public Parent addContent(Collection<? extends Content> c);
public Parent addContent(int index, Content child);
public Parent addContent(int index, Collection<? extends Content> c);
void canContainContent(Content content, int index, boolean replace); // 检查content是否能在index位置插入或替换,不能发生异常
}
public class Document extends CloneBase implements Parent {
public Document();
public Document(Element rootElement, DocType docType, String baseURI);
public Document(Element rootElement, DocType docType);
public Document(Element rootElement);
public Document(List<? extends Content> content);
public void setProperty(String id, Object value); // 为当前document添加一些额外属性,可供分析时使用
public Object getProperty(String id);
public boolean hasRootElement();
public Element detachRootElement();
public Element getRootElement();
public Document setRootElement(Element rootElement);
public DocType getDocType();
public Document setDocType(DocType docType);
}
public class Element extends Content implements Parent {
public Element(final String name, final Namespace namespace);
public Element(final String name);
public Element(final String name, final String uri);
public Element(final String name, final String prefix, final String uri);
public boolean isAncestor(final Element element); // 是不是某个element的祖先
public boolean isRootElement();
public String getName(); // 返回不带前缀标签名
public String getQualifiedName(); // 返回带前缀的标签名
public Element setName(final String name);
public Namespace getNamespace();
public Element setNamespace(Namespace namespace);
public String getNamespacePrefix();
public String getNamespaceURI();
public String getText();
public String getTextTrim();
public String getTextNormalize();
public String getChildText(final String cname); // getChild(cname).getText()
public String getChildTextTrim(final String cname);
public String getChildTextNormalize(final String cname);
public String getChildText(final String cname, final Namespace ns);
public String getChildTextTrim(final String cname, final Namespace ns);
public String getChildTextNormalize(final String cname, final Namespace ns);
public Element setText(final String text);
public boolean hasAttributes();
AttributeList getAttributeList();
public List<Attribute> getAttributes();
public Attribute getAttribute(final String attname);
public Attribute getAttribute(final String attname, final Namespace ns);
public String getAttributeValue(final String attname);
public String getAttributeValue(final String attname, final String def);
public String getAttributeValue(final String attname, final Namespace ns);
public String getAttributeValue(final String attname, final Namespace ns, final String def);
public Element setAttributes(final Collection<? extends Attribute> newAttributes);
public Element setAttribute(final String name, final String value);
public Element setAttribute(final String name, final String value, final Namespace ns);
public boolean removeAttribute(final String attname);
public boolean removeAttribute(final String attname, final Namespace ns);
public boolean removeAttribute(final Attribute attribute);
// 与getContext相关方法的返回子节点,与getChild相关的方法返回子element节点
public List<Element> getChildren();
public List<Element> getChildren(final String cname);
public List<Element> getChildren(final String cname, final Namespace ns);
public Element getChild(final String cname, final Namespace ns);
public Element getChild(final String cname);
public boolean removeChild(final String cname);
public boolean removeChild(final String cname, final Namespace ns);
public boolean removeChildren(final String cname);
public boolean removeChildren(final String cname, final Namespace ns);
}
JDOM也支持将Doument保存到xml文件,首先创建一个输出对象XMLOutputter,然后调用其output(Doument document, Writer writer / OutputStream os)方法写入文件。
JDOM也支持XPath操作,早期XPath可以直接调用其静态方法selectNodes与selectSingleNode解析,简单方便,但现已被废弃这种操作方式。代替方式需要引入jaxen包,通过XPathFactory的静态方法instance()得到一个XPathFactory对象,并根据XPathFactory对象的compile方法获取一个XPathExpression对象,通过XPathExpression对象的evaluate方法来进行解析。
public abstract class XPathFactory {
public static final XPathFactory instance();
public XPathExpression<Object> compile(String expression);
}
public interface XPathExpression<T> extends Cloneable {
public List<T> evaluate(Object context);
public T evaluateFirst(Object context); // 取evaluate的第一个
}
DOM4J(Document Object Model for Java)解析方式
DOM4J与其他几种解析方式相比,其性能最好,既支持对象模型又支持事件模型,故得到了广泛的应用,在进行XML解析,若无特殊需求,技术选型就选DOM4J。
DOM4J获取Document对象的方式主要有以下几种,第一种是从XML文件解析得到,先new一个SAXReader对象,然后调用其read方法解析得到Document对象;第二种是解析xml格式的String,直接调用DocumentHelper的静态方法parseText获取Document对象;第三种是直接调用DocumentHelper的静态方法createDocument创建一个空的Document对象;还有一种是从DOM的Document转换为DOM4J的Document,通过new一个DOMReader对象,然后调用其read方法得到,其具体操作与SAXReader相似。
生成xml文件,只需要调用document的write(Writer writer)方法即可(期数write方法是node定义的,也就是说可以只写入某个节点到writer)。
// 从XML文件创建Document对象
public class SAXReader {
public SAXReader();
public Document read(File file / URL url / InputStream in / Reader reader / InputSource in);
public ErrorHandler getErrorHandler();
public void setErrorHandler(ErrorHandler errorHandler);
public EntityResolver getEntityResolver();
public void setEntityResolver(EntityResolver entityResolver);
// ElementHandler可以在解析xml时对Element做额外的操作,不会影响生成Element的过程,但可以对生成的Element进行修改
public void setDefaultHandler(ElementHandler handler); // 所有element节点都会调用handler处理
public void addHandler(String path, ElementHandler handler); // path路径(绝对路径如/root/parent/node)的elemnt会进行handler处理,
// 被处理的路径及其下面路径(如/root/parent/node/child)的element都不会再调用默认的ElementHandler了,
// 该函数虽然是add方法,但相同的路径,后面的会覆盖前面的handler
public void removeHandler(String path);
public void resetHandlers();
public void setProperty(String name, Object value);
public void setFeature(String name, boolean value);
... 另外有一系列函数,用来为创建的解析器的解析细节设置控制参数
}
public interface ElementHandler {
void onStart(ElementPath elementPath); // 可以获取Attribute但内容与子Element这里都无法获取,需要到end中获取
void onEnd(ElementPath elementPath);
}
public interface ElementPath {
int size(); // 路径级数如/a/b/c为3
Element getElement(int depth); // 获取第n级元素,如depth为0返回根元素
String getPath();
Element getCurrent(); // 当前element
void addHandler(String path, ElementHandler handler); // path可以再用相对当前路径的相对路径如c/e
void removeHandler(String path);
}
// 帮助创建DOM4J的对象,以及XPath的简化操作
public final class DocumentHelper {
// 创建DOM4J相应的对象,都不需用new,都用该类的静态方法创建
public static Document parseText(String text); // 从xml文本字符串解析得到Document
public static Document createDocument(); // 创建一个空的Document对象
public static Document createDocument(Element rootElement); // 创建一个包含根元素的Document
public static Element createElement(QName qname);
public static Element createElement(String name);
public static Element makeElement(Branch source, String path);
public static Attribute createAttribute(Element owner, QName qname, String value);
public static Attribute createAttribute(Element owner, String name, String value);
public static CDATA createCDATA(String text);
public static Comment createComment(String text);
public static Text createText(String text);
public static Entity createEntity(String name, String text);
public static Namespace createNamespace(String prefix, String uri);
public static ProcessingInstruction createProcessingInstruction(String pi, String d);
public static ProcessingInstruction createProcessingInstruction(String pi, Map data);
public static QName createQName(String localName, Namespace namespace);
public static QName createQName(String localName);
// 对应XPath的方法
public static XPath createXPath(String xpathExpression);
public static XPath createXPath(String xpathExpression, VariableContext context);
public static NodeFilter createXPathFilter(String xpathFilterExpression);
public static Pattern createPattern(String xpathPattern);
public static List selectNodes(String xpathFilterExpression, List nodes);
public static List selectNodes(String xpathFilterExpression, Node node);
public static void sort(List list, String xpathExpression);
public static void sort(List list, String expression, boolean distinct);
}
在DOM中,Node表示的节点可以挂载到其他节点上,也可以挂载其他节点,而在JDOM中,节点Content表示可以挂载在容器下面,而容器Parent表示可以挂载节点,而Parent不是Content的子类,在DOM4J中,节点Node表示普通节点,而容器Branch是Node的子类,这更符合实际模型。
Node直接子类有Attribute、DocumentType、Entity、ProcessingInstruction,另外还有直接子类Branch与CharacterData。Branch表示节点可以作为容器,其子类有Document与Element。CharacterData有子类CDATA、Comment与Text。
public interface Node extends Cloneable {
String asXML(); // 将节点转换为xml字符串
void write(Writer writer); // 将节点xml格式写入流
void accept(Visitor visitor); // 当前Node以及其子节点都会根据其节点类型调用Vistor相应的visit方法
Object clone(); // 深克隆
// 节点基本信息
short getNodeType();
String getNodeTypeName();
String getName();
void setName(String name);
String getText();
void setText(String text);
// XPath相关
XPath createXPath(String xpathExpression);
String getPath(); // 绝对xpath
String getPath(Element element); // 相对xpath
String getUniquePath();
String getUniquePath(Element element);
boolean matches(String xpathExpression);
// 父子节点相关
Element getParent();
void setParent(Element element);
Document getDocument();
void setDocument(Document document);
boolean hasContent(); // content表示子节点
Node detach(); // 解除绑定,从父容器中移除
List selectNodes(String xpathExpression);
List selectNodes(String xpathExpression, String comparisonXPathExpression);
List selectNodes(String xpathExpression, String comparisonXPathExpression, boolean removeDuplicates);
Node selectSingleNode(String xpathExpression);
}
public interface Branch extends Node {
Node node(int var1); // 第i个子节点
int indexOf(Node var1);
int nodeCount();
List content();
void setContent(List contents);
Iterator nodeIterator();
void appendContent(Branch branch);
void clearContent();
List processingInstructions();
List processingInstructions(String target);
ProcessingInstruction processingInstruction(String target);
void setProcessingInstructions(List listOfPIs);
Element addElement(String name / QName qName); // 新建一个名为name的element
Element addElement(String qualifiedName, String namespaceURI);
void add(Node node / Comment comment / Element element / ProcessingInstruction pi);
boolean remove(Node node / Comment comment / Element element / ProcessingInstruction pi / String target);
}
public interface Document extends Branch {
Element getRootElement();
void setRootElement(Element rootElement);
Document addComment(String comment);
Document addProcessingInstruction(String target, String text);
Document addProcessingInstruction(String taget, Map data);
Document addDocType(String name, String publicId, String systemId);
DocumentType getDocType();
void setDocType(DocumentType docType);
EntityResolver getEntityResolver();
void setEntityResolver(EntityResolver resolver);
String getXMLEncoding();
void setXMLEncoding(String encoding);
}
public interface Element extends Branch {
boolean isRootElement();
boolean hasMixedContent(); // 是否既有子element又有文本
boolean isTextOnly();
Element createCopy(); // 深克隆并detach
Element createCopy(String name / QName qName); // 深克隆并detach,并改名
QName getQName();
QName getQName(String qualifiedName);
String getQualifiedName();
void setQName(QName qualifiedName);
String getText();
String getTextTrim();
Object getData();
void setData(Object var1);
// 元素定义名字空间的prefix不会重复,但uri可能重复,也就是说多个prefix可以对应同一个uri,但不可以多个uri对应同一个prefix
Namespace getNamespace(); // 当前QName的prefix对应的那一个名字空间
Namespace getNamespaceForPrefix(String prefix);
List getNamespacesForURI(String uri);
Namespace getNamespaceForURI(String uri); // 如果不止一个,返回的是随机的
String getNamespacePrefix();
String getNamespaceURI();
List additionalNamespaces();
List declaredNamespaces();
Element addAttribute(String name, String value);
Element addAttribute(QName qName, String value);
Element addComment(String comment);
Element addCDATA(String cdata);
Element addEntity(String name, String text);
Element addNamespace(String prefix, String uri);
Element addProcessingInstruction(String taget, String text);
Element addProcessingInstruction(String taget, Map data);
Element addText(String text);
void add(Attribute attribute / CDATA cdata / Entity entity / Text text / Namespace ns);
boolean remove(Attribute attribute / CDATA cdata / Entity entity / Text text / Namespace ns);
List attributes();
void setAttributes(List attrs);
void appendAttributes(Element element); // 将element的所有attribute添加到当前element
int attributeCount();
Iterator attributeIterator();
Attribute attribute(int index / String name / QName qName);
String attributeValue(String name / QName qName);
String attributeValue(String name / QName qName, String defaultValue);
List elements();
List elements(String name / QName qName);
Iterator elementIterator();
Iterator elementIterator(String name / QName qName);
Element element(String name / QName qName);
String elementText(String name / QName qName);
String elementTextTrim(String name / QName qName);
}
DOM4J对XPath支持同JDOM一样需要引入jaxen包。首先通过DocumentHelper的静态方法createXPath创建XPath对象,然后通过XPath对象的e,但Node节点已经有根据XPath表达式过滤的方法,所以XPath类在DOM4J中很少用。
public interface XPath extends NodeFilter {
String getText(); // 返回XPath表达式
boolean matches(Node node); // 节点是否匹配该XPath表达式
Object evaluate(Object context); // 根据上下文解析表达式,取代了Object selectObject(Object context)方法,返回值可能是List、Node、String、Number、boolean,后面的方法大多根据该方法强转得到
List selectNodes(Object context); // 获取节点列表
List selectNodes(Object context, XPath sortXPath); // 同sortXPath.sort(selectNodes(context));
List selectNodes(Object context, XPath sortXPath, boolean distinct); // 同sortXPath.sort(selectNodes(context), distinct);
Node selectSingleNode(Object context); // 获取selectNodes的第一个Node
String valueOf(Object context); // 解析获取String
Number numberValueOf(Object context); // 解析获取Number值
boolean booleanValueOf(Object context); // 解析获取boolean值
void sort(List list, boolean distinct); // 当作为sortXPath的时候需要实现该方法,distinct为是否去重
void sort(List list); // sort(list, true)
}