文章目录
xml(Extensive Markup Language 可扩展标记语言)
- 作用
- 用来保存数据
- 用来做配置文件
- 数据传输载体
HTML注重于数据的显示,XML注重于数据的存储。
一、XML文档规则
1、文档声明
<?xml version="1.0" encoding="utf-8" standalone="no"?>
version: 解析该xml时,使用什么版本的解析器
encoding: 该文档的解码方式
standalone: 该文档是否关联其他文档
2、标签(元素)定义
-
<> 括起来的元素,成对出现,如
-
文档声明下面的第一个标签称为根标签,有且仅有一个
-
空标签,既是开始标签也是结束标签,如
-
标签名可以自定义
XML 命名规则:
- 名称可以含字母、数字以及其他的字符
- 名称不能以数字或者标点符号开始
- 名称不能以字符 “xml”(或者 XML、Xml)开始
- 名称不能包含空格
- 尽量简单且见名知意
3、标签属性
- 属性通常提供属于数据组成部分的信息,如果属性值里包含的信息属于该实体本身,则应该使用子元素来指定该信息,因此,W3C推荐尽量使用子元素,而避免使用属性
- 定义在标签里面,<元素名称,属性名称=“属性值”> 内容</元素名称>
如: <stu id=“1002”> 小明
4、xml注释
与HTML类似,不允许放在文档第一行,必须放在文档声明下面
如: <!–注释内容–>
5、实体与CDATA标记
严格地讲,在 XML 中仅有字符 “<“和”&” 是非法的。省略号、引号和大于号是合法的,但是把它们替换为实体引用是个好的习惯。
实体引用 | 所表示的符号 |
---|---|
< | < |
& | & |
> | > |
' | ‘(英文单引号) |
" | "(英文双引号) |
如果某段字符串里面有过多的字符,并且里面包含了类似标签或者关键字的这种文字,不想让xml的解析器去解析。那么可以使用CDATA来包装。不过这个CDATA一般比较少看到。通常在服务器给客户端返回数据的时候。
-
语法
<![CDATA[文本内容]]>
如:
<![CDATA[<a href=“http://www.baidu.com”>I Love My Home]]>
6、命名空间
解决同名的标签名。
- 语法:
xmlns[:prefix] = "命名空间字符串"
xmlns:bb="http://www.moc.com/teach"
xmlns:aa="http://www.moc.com/teach"
<aa:name></aa:name>
<bb: name></bb:name>
二、XPath
XPath语言是一门专门用于在XML文档中查找信息的语言,其他XML程序可利用XPath在XML文档中对元素和属性进行导航。
- 为什么要查找标签和属性?
XML文档是用来存储数据的,需要将数据提取出来使用,所以通过查找标签和属性进一步获取数据。
1、XPath节点
节点类型 | 描述 |
---|---|
XML文档根节点 | XML文档的根称为文档节点或根节点 |
元素节点 | 一个元素的开始标签、结束标签,以及开始标签和结束标签之间的全部内容整体 |
属性节点 | 元素的每个属性,属性节点包括属性名和属性值两个部分。属性节点必须依附于元素节点 |
注释节点 | 文档中<!–和->包含的部分,注释对应的就是注释节点 |
命名空间节点 | 代表文档中的xmlns:prefix属性 |
文本节点 | 元素中间的字符数据,包括CDATA段中的字符数据 |
2、基本概念
-
基本值(原子值)
用于表示简单的数据值,例如整数值、字符串等。可以把基本值当成没有父节点且没有子节点的节点。
-
项
XPath2.0提出的一个术语,一个项代表一个节点或基本值。
-
节点集和序列
- XPath1.0里将XPath表达式表示多个节点称为节点集
- XPath2.0提出的序列可以代表一个普通的项,也可以代表节点集
-
节点关系
- 父节点
- 子节点
- 兄弟节点
- 祖先节点
- 后代节点
-
相对路径和绝对路径
绝对路径以斜线(/)开头,如/stu/小明/age
3、基础语法
XPath使用路径表达式来定位XML文档中的节点或节点集,每个Xpath表达式总由多个步(step)组成,多个步之间用斜线分隔
- 格式:
轴::节点测试[ 限定谓语 ]
- 轴
XPath的步使用轴来定义所选节点与当前节点之间的结构关系
轴名 | 描述 |
---|---|
ancestor | 祖先节点 |
ancestor-or-self | 祖先节点并包含自身 |
attribute | 选择节点的所有属性 |
child | 选取当前节点的所有子节点 |
parent | 选取当前节点的父节点 |
descendant | 当前节点的所有后代节点 |
descendant-or-self | 当前节点的所有后代节点并包含自身 |
self | 当前节点自身 |
following-sibling | 选择当前节点的兄弟节 |
-
节点测试
节点测试用于从指定轴所匹配的节点集中选出特定的节点。
节点测试名 | 描述 | 实例 |
---|---|---|
nodename | 从轴匹配的所有节点中child:book选出具有nodename的节点 | child::student |
node() | 选择指定轴匹配的所有类型节点 | child::node() |
text() | 选择指定轴匹配的所有文本类型节点 | child::text() |
comment() | 选择指定轴匹配的所有注释节点 | descendant::comment() |
* | 通配符,不进地任何过滤 | child:* |
-
限定谓语
限定谓语是一个boolean表达式,或者可以转换为boolean值的表达式, 用于进一步提炼所选的节点集。限定谓语应该放在括号中
语法
child::student[1] 或 child::student[ positio()=1 ]
4、运算符和函数
-
算术运算符
+(加)、-(减)、*(乘)、div(除)、mod(求余)
-
比较运算符
=(等于)、!=(不等于)、<(小于)、<=(不大于)、>(大于)、>=(不小于)
-
逻辑运算符
or(或)、and(与)
-
组合运算符 |
可用于组合多个路径表达式,通过“I"运算符,一次选取若干个路径
如: student[position() = 1] | name[position() = last() ]
-
常见函数
函数名 描述 fn:position() 返回当前正在被处理的节点在父节点中的index值 fn:last() 返回当前正在被处理的节点列表中项的数目 fn:name() 返回当前节点的名称或指定节点集中第一个节点的名称 fn:root() 通常返回文档根节点
三、XML约束
下面的文档,属性的ID值是一样的。现实中是不可能出现的,并且第二个学生的姓名有好几个,一般也很少。那么怎么规定ID的值唯一,或者是元素只能出现一次,不能出现多次?这是需要该XML文档进行约束。
<stus>
<stu id="18086">
<name>张三</name>
<age>18</age>
<address>深圳</address>
</stu>
<stu id="18086">
<name>李四</name>
<name>李五</name>
<name>李六</name>
<age>28</age>
<address>北京</address>
</stu>
</stus>
-
DTD(文档类型定义)
定义XML文档的合法构建模块。
它使用一系列的合法元素来定义文档结构。
语法自成一派,出现的早。可读性比较差。 -
Schema
XML Schema 语言也可作为 XSD(XML Schema Definition)来引用。
其实就是一个xml,使用xml的语法规则,xml解析器解析起来比较方便,是为了替代DTD。
但是Schema约束文本内容比DTD的内容还要多,所以目前也没有真正意义上的替代DTD。
1、DTD简介
- 引入外部的DTD
<!-- stus.dtd文件:-->
<!ELEMENT stus (stu, stu)>
<!ELEMENT stu (name, age)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!-- stus.xml文件: -->
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!--文档类型 根标签名称 网络上的dtd dtd的名称 dtd的路径
<!DOCTYPE stus PUBLIC "//UNKNOWN/" "unknown.dtd"> -->
<!--文档类型 根标签名称 本地的dtd dtd的路径 -->
<!DOCTYPE stus SYSTEM "stus.dtd">
<stus>
<stu>
<name>张三</name>
<age>18</age>
</stu>
<stu>
<name>李四</name>
<age>16</age>
</stu>
</stus>
- xml嵌入DTD
<!-- stus.xml文件:-->
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE stus [
<!ELEMENT stus (stu, stu)>
<!ELEMENT stu (name, age)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
]>
<stus>
<stu>
<name>张三</name>
<age>18</age>
</stu>
<stu>
<name>李四</name>
<age>16</age>
</stu>
</stus>
- DTD基本语法
<!-- stus.dtd文件:-->
<?xml version="1.0" encoding="utf-8" ?>
<!--声明元素 <!ELEMENT 元素名称 类别> -->
<!ELEMENT stus (stu)+> <!-- +:一个或多个 *:零个或多个 ?:零个或一个 -->
<!ELEMENT stu (name, age)> <!-- stu下面有两个元素name,age;逗号(,): 顺序为name-age , |:表示任意一个-->
<!ELEMENT name (#PCDATA)> <!-- (#PCDATA):name为包含内容的简单标签 -->
<!ELEMENT age (#PCDATA)>
<!--声明属性 <!ATTLIST 元素名称 属性名称 属性类型 默认值> -->
<!ATTLIST stu id CDATA #IMPLIED> <!-- stu可以有属性id,其值的类型为CDATA, CDATA:字符数据 #IMPLIED:属性不是必需的 -->
<!-- <!ATTLIST stu id ID #REQUIRED>
ID: 值为唯一的 id
#REQUIRED: 属性值是必需的
ID 类型的属性必须为 #REQUIRED
id属性的值前加上"字母"或者_以符合XML命名规范再次验证则可通过
-->
<!-- stus.xml文件: -->
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE stus SYSTEM "stus.dtd">
<stus>
<stu id="c102">
<name>张三</name>
<age>18</age>
</stu>
<stu id="c1003">
<name>李四</name>
<age>16</age>
</stu>
</stus>
2、 schema(xsd)简介
<!-- teacher.xsd文件:-->
<?xml version="1.0" encoding="utf-8" ?>
<!-- xmlns: xml namespace 命名空间
targetNamespace: 目标名称空间。下面定义的那些元素都与这个名称空间绑定上
elementFormDefault: 元素的格式化情况
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.w3school.com.cn"
elementFormDefault="qualified">
<!-- 复杂元素 -->
<element name="teachers">
<complexType> <!-- 声明复杂元素 -->
<sequence maxOccurs="unbounded"> <!--包裹的标签可以重复任意次-->
<!-- 复杂元素 -->
<element name="teacher">
<complexType>
<sequence>
<!--下面为简单元素 -->
<element name="name" type="string"></element>
<element name="age" type="int"></element>
</sequence>
</complexType>
</element>
</sequence>
</complexType>
</element>
</schema>
<!-- teacher.xml文件:-->
<?xml version="1.0" encoding="utf-8" ?>
<teachers
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.w3school.com.cn"
xsi:schemaLocation="http://www.w3school.com.cn teacher.xsd">
<teacher>
<name>James</name>
<age>28</age>
</teacher>
<teacher>
<name>Danial</name>
<age>27</age>
</teacher>
</teachers>
四、XML的解析
就是获取元素里面的字符数据和属性数据,常用解析方式有如下两种:
-
DOM (document object model)
把整个xml全部读到内存当中,形成树状结构。整个文档称之为document对象,属性对应Attribute对象,所有的元素节点对应Element对象,文本也可以称之为Text对象,以上所有对象都可以称之为Node节点。如果xml特别大,那么将会造成内存溢出。可以对文档进行增删操作
-
SAX (sample API for XML)
基于事件驱动,读取一行解析一行,不会造成内存溢出。
1、JAXP
Java解析XML文档的API称为JAXP,它的全称是Java API for XML。
-
JAXP往往作为JDK的一部分发布,但它仅仅是一些API接口,并未提供真正的实现,因此实际使用过程中还需要具体的解析实现。
-
JAXP允许应用程序在不同的XML解析器之间切换
-
JAXP提供的与解析相关的类
DocumentBuilderFactory: 获取DOM解析工厂类
DocumentBuilder: DOM解析器标准接口
SAXParserFactory: 获取SAX解析器的工厂类
SAXParser: SAX解析器的标准接口
2、SAX解析XML
-
流程
解析XML文档时,SAX解析器负责在XML文档中”行走”,每当遇到文档开始、元素开始、文本、元素结束和文档结束时,都将负责向发送事件,而程序员则负责提供事件监听器来监听这些事件,并通过事件获取XML文档信息。
-
JAXP为SAX解析器提供的2组APl:
- XMLReader和XMLReaderFactory:XMLReaderFactory工厂类的createXMLReaderO静态方法用于创建XMLReader
- SAXParser和SAXParserFactory:SAXParserFactoryI厂类的newSAXParser()实例方法用于创建SAXParser(推荐)
-
SAXParser解析XML文档的方法:
void parse(File f, DefaultHandler dh): 使用指定的dh作为监听器监听SAX解析事件,解析XML文档。
-
SAX解析事件
监听器 | 描述 |
---|---|
ContentHandler | 监听XML文档内容处理事件的监听器 |
DTDHandler | 监听DTD处理事件的监听器 |
EntityResolver | 监听实体处理事件的监听器 |
ErrorHandler | 监听解析错误的监听器 |
JAXP提供了一个DefaultHandler类,这个类实现了上述4个监听器接口,并为监听器接口中所包含的方法提供了空实现。
- SAX解析XML文档步骤
- 在工程中引入Xerces-J具体解析器实现类 jar包 xml-apis.jar
- 自定义事件监听器继承自DefaultHandler
- 通过SAXParseFactory的newlnstance()方法创建SAX解析器工厂对象
- 通过SAXParseFactory对象的newSAXParser())方法创建SAXPasrer对象
- 调用SAXParser对象的parse()方法解析XML文档
public class SAXParse {
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
// 创建SAX解析器工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
// 创建SAX解析器
SAXParser parser = factory.newSAXParser();
// 开始解析XML文档
parser.parse("C:\\Users\\Administrator\\Desktop\\1.xml", new mocHandler());
}
}
// 具体解析方法
public class mocHandler extends DefaultHandler {
// 定义一个变量来保存当前正在处理的tag
private String currentTag;
// 每当处理文本数据时将触发该方法
@Override
public void characters(char[] ch, int start, int lenght){
String content = new String(ch, start, lenght);
if(content.trim().length() > 0){
System.out.println("<"+currentTag+">元素的值是:"+content.trim());
}
}
// 每当文档解析结束时触发该方法
@Override
public void endDocument(){
System.out.println("解析文档结束。");
}
// 解析元素结束时触发该方法
@Override
public void endElement(String uri, String localName, String qName){
System.out.println("处理元素结束:"+qName);
}
// 解析文档开始时触发该方法
@Override
public void startDocument(){
System.out.println("解析文档开始。");
}
//解析元素开始
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes){
System.out.println("开始处理元素:"+qName);
currentTag = qName;
if(attributes.getLength() > 0){
System.out.println("<"+ currentTag +">元素属性如下:");
for (int i = 0; i < attributes.getLength(); i++) {
System.out.println(attributes.getQName(i) + "---->" + attributes.getValue(i));
}
}
}
}
- SAX生成XML文档步骤
- 创建保存xml的结果流对象
- 获取sax生成工厂对象实例
- 获取sax生成处理者对象实例
- 获取sax生成器
- 生成文档及文档中的元素
public class BuildXML {
public static void main(String[] args) throws TransformerConfigurationException, SAXException {
// 创建保存XML的结果流对象
Result resultXML = new StreamResult(new File("C:\\2.xml"));
// 获取sax生成工厂实例
SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
// 获取SAX生成处理者对象实例
TransformerHandler transformerHandler = saxTransformerFactory.newTransformerHandler();
transformerHandler.setResult(resultXML); // 保存xml的结果流对象
// 获取SAX生成器
Transformer transformer = transformerHandler.getTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); // 允许生成xml时生成额外的空格
// 生成文档的开始
transformerHandler.startDocument();
AttributesImpl attributes = new AttributesImpl();
AttributesImpl attrImpl = new AttributesImpl();
attributes.addAttribute("", "", "语言", "", "中文");
transformerHandler.startElement("", "", "书籍列表", attributes);
// 第一本书
transformerHandler.startElement("", "", "四大名著", attrImpl);
transformerHandler.startElement("", "", "书名", attrImpl);
transformerHandler.characters("红楼梦".toCharArray(), 0, "红楼梦".length());
transformerHandler.endElement("", "", "书名");
transformerHandler.startElement("", "", "作者", attrImpl);
transformerHandler.characters("曹雪芹".toCharArray(), 0, "曹雪芹".length());
transformerHandler.endElement("", "", "作者");
transformerHandler.startElement("", "", "价格", attrImpl);
transformerHandler.characters("85.00".toCharArray(), 0, "85.00".length());
transformerHandler.endElement("", "", "价格");
transformerHandler.endElement("", "", "四大名著");
// 第二本书
transformerHandler.startElement("", "", "四大名著", attrImpl);
transformerHandler.startElement("", "", "书名", attrImpl);
transformerHandler.characters("西游记".toCharArray(), 0, "西游记".length());
transformerHandler.endElement("", "", "书名");
transformerHandler.startElement("", "", "作者", attrImpl);
transformerHandler.characters("罗贯中".toCharArray(), 0, "罗贯中".length());
transformerHandler.endElement("", "", "作者");
transformerHandler.startElement("", "", "价格", attrImpl);
transformerHandler.characters("48.00".toCharArray(), 0, "48.00".length());
transformerHandler.endElement("", "", "价格");
transformerHandler.endElement("", "", "四大名著");
transformerHandler.endElement("", "", "书籍列表");
// 文档生成结束
transformerHandler.endDocument();
System.out.println("XML文档生成成功。");
}
}
3、Dom4j解析XML
element.element(“stu”): 返回该元素下的第一个stu元素
element.elements(): 返回该元素下的所有子元素
- 创建SaxReader对象(需先导入dom4j-1.6.1.jar包)
- 指定解析的xml
- 获取根元素。
- 根据根元素获取子元素或者下面的子孙元素
try {
// 1.创建sax读对象
SAXReader reader = new SAXReader();
// 2.指定解析XML的解析源
Document document = reader.read(new File("D:\\stus.xml"));
// 3. 得到元素
Element rootElement = document.getRootElement(); // 获取根元素
//System.out.println(rootElement.getName());
Element ageElement = rootElement.element("stu").element("age"); // 获取根元素下的stu元素下的age元素
//System.out.println(ageElement.getText()); // 获取标签的文本内容
// 获取根元素所有的子元素
List<Element> elements = rootElement.elements();
for (Element element : elements) {
String name = element.element("name").getText();
String age = element.element("age").getText();
String address = element.element("address").getText();
System.out.println("name="+name+"---age="+age+"---address="+address);
}
} catch (Exception e) {
e.printStackTrace();
}
dom4j支持xpath的写法,能够快速的定位到具体的某一个元素
- 添加jar包依赖 jaxen-1.1-beta-6.jar
- 在查找指定节点的时候,根据xPath语法规则来查找
- 后续的代码与上一个的解析代码一样。
try {
// 1.创建sax读对象
SAXReader reader = new SAXReader();
// 2.指定解析XML的解析源
Document document = reader.read(new File("D:\\stus.xml"));
// 3. 得到标签
Element rootElement = document.getRootElement(); // 获取根元素
// 使用Xpath,需添加支持的jar包jaxen-1.1-beta-6.jar, 下面参数为XPath表达式
Element nameElement = (Element) rootElement.selectSingleNode("//name");
System.out.println(nameElement.getText());
System.out.println("--------------");
// 获取文档里的所有元素
List<Element> list = rootElement.selectNodes("//name");
for (Element element : list) {
System.out.println(element.getText());
}
} catch (Exception e) {
e.printStackTrace();
}