xml文档编写
xml语法
一个xml文件分为如下几部分内容:
- 文档声明
- 元素
- 属性
- 注释
- CDATA区
- 特殊字符
文档声明
在编写xml文档时,需要先使用文档声明定义xml版本以及编码格式等信息。例如:
<?xml version="1.0" encoding="GB2312"?>
如果不声明编码默认使用utf-8
。
用standalone属性说明文档是否独立,是否依赖其他文档。
<?xml version="1.0" standalone="yes"?>
元素
xml元素是指文件中出现的标签,分为起始标签和结束标签。标签可以包含主体,也可以不包含主体。
<tag1>content</tag1>
<tag2></tag2>
标签可以嵌套。
一个xml文档必须有且仅有一个根标签。其他标签都是这个根标签的子标签。
元素标签的命名规范:
- 区分大小写,和是两个不同的标签。
- 不能以下划线
(_)
开头。 - 不能以xml(或XML、Xml等)开头。
- 不能包含空格。
- 名称中间不能包含冒号
(:)
。
元素内容中,制表符和换行符都会被保留。
属性
一个元素可以包含多个属性,但是属性不允许重复。属性的值需要用引号(单引号或双引号)标出。
例如:
<tag1 name="value">content</tag2>
属性的命名规范与元素标签的命名规范一致。
注释
关于注释的语法是:
<!--这是注释-->
<tag1>content</tag1>
CDATA区
CDATA区段的文本不会被解析器解析,用于传递特殊字符,例如<
、>
等。
<![CDATA[内容]]>
特殊字符
对于特殊字符,必须进行转义:
特殊字符 | 代替符号 |
---|---|
& | & |
< | < |
> | > |
" | " |
’ | &apos |
xml DTD
DTD(Document Type Definition): 文档定义类型。
作用:约束xml的书写规范。
注意:dtd可以写在单独的文件中,扩展名是dtd,并且该文件必须使用utf-8编码。
<!DOCTYPE 货架 [
<!ELEMENT 货架 (商品+)>
<!ELEMENT 商品 (商品名,产地,售价)>
<!ELEMENT 商品名 (#PCDATA)>
<!ELEMENT 产地 (#PCDATA)>
<!ELEMENT 售价 (#PCDATA)>
<!ATTLIST 商品
条形码 ID #REQUIRED
厂家 CDATA #IMPLIED
包装 CDATA #FIXED "塑料袋">
<!ENTITY copyright "加点糖博客">
]>
xml使用DOCTYPE语句来指明它遵循的DTD文档。
- 当引用的DTD文档在本地时:
<!DOCTYPE 根元素 SYSTEM “goods.dtd”>
- 当引用的DTD文档在远程时:
<!DOCTYPE 根元素 PUBLIC "DTD名称" "DTD文档URL">
DTD约束可以在XML文档中直接定义,在文档中定义时,对编码没有要求。
DTD定义元素
语法:<!ELEMENT 元素名称 使用规则>
使用规则
(#PCDATA)
: 元素主体只能是普通文本(parsed character data)EMPTY
: 元素主体为空。ANY
: 元素的主体为任意类型。(子元素)
: 指示元素中包含的子元素。
子元素的描述
- 子元素用逗号","分开,必须按照声明顺序去编写xml文档
- 如果子元素用"|"分开,说明任选其一。
- 用
+*?
来表示元素出现的次数(默认表示出现一次,+
表示至少出现一次,*
表示可有可无,?
表示0次或1次)
下面给出示例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE 货架 [
<!ELEMENT 货架 (商品+)>
<!ELEMENT 商品 (商品名,产地,售价)>
<!ELEMENT 商品名 (#PCDATA)>
<!ELEMENT 产地 (#PCDATA)>
<!ELEMENT 售价 (#PCDATA)>
<!ATTLIST 商品
条形码 ID #REQUIRED
厂家 CDATA #IMPLIED
包装 CDATA #FIXED "塑料袋">
<!ENTITY copyright "加点糖博客">
]>
<货架>
<商品 包装="塑料袋" 厂家="苏泊尔" 条形码="SN123">
<商品名>电饭煲</商品名>
<产地>杭州</产地>
<售价>199</售价>
</商品>
<商品 包装="塑料袋" 条形码="SN456">
<商品名>博客</商品名>
<产地>©right;</产地>
<售价>199</售价>
</商品>
</货架>
xml Schema
xml schema 文件本身就是一个xml文件,但它的扩展名通常为.xsd
。
一个xml schema 文档通常称为模式文档(约束文档)
,遵循这个文档书写的xml文件称之为实例文档
。
和xml文档一样,一个xml schema文档也必须有一个根节点,但这个根节点的名字固定为schema
。
编写了一个xml schema约束文件后,通常需要把这个文件中声明的元素绑定到一个URL地址上,xml schema技术中有一个专业术语来描述这个过程,即把xml schema文档声明的元素绑定到一个名称空间上,以后xml文件可以通过这个URL(即名称空间)来告诉解析引擎,xml文件中编写的元素的约束是怎样的。
(以下代码未验证)
.xsd
文件的示例:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://addsomesugar.csdn.com"
elementFormDefault="qualified">
<xs:element name="货架">
<xs:complexType>
<xs:sequence maxOccurs="unbounded">
<xs:element name="商品">
<xs:complexType>
<xs:sequence>
<xs:element name="商品名" type="xs:string" />
<xs:element name="产地" type="xs:string" />
<xs:element name="售价" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
xml实例文件:
<?xml version="1.0" encoding="UTF-8" ?>
<addsomesugar:货架 xmlns:addsomesugar="http://addsomesugar.csdn.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://localhost:63342/test1 test1.xsd">
<addsomsugar:商品>
<addsomesugar:商品名>andromeda:电饭煲</addsomesugar:商品名>
<addsomesugar:产地>andromeda:杭州</addsomesugar:产地>
<addsomesugar:售价>199</addsomesugar:售价>
</addsomsugar:商品>
</addsomesugar:货架>
xml文档解析
DOM解析方式
根据xml层级结构在内存中分配一个树形结构。
注意:DOM会将xml文档一次性全部读进来。
xml DOM的每个元素,都会被解析为一个节点Node,而常用的节点类型又分为
- 元素节点 Element
- 属性节点 Attr
- 文本节点 Text
- 文档节点 Document
DOM解析方式的优点:分配了整个树形结构,很方便地实现增删改等操作。
DOM解析方式的缺点:如果要解析地xml文件过大,会导致内存不够用。
例如,java对于xml文件的dom解析:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<货架>
<商品 包装="塑料袋" 厂家="苏泊尔" 条形码="SN123">
<商品名>电饭煲</商品名>
<产地>杭州</产地>
<售价>199</售价>
</商品>
<商品 包装="塑料袋" 条形码="SN456">
<商品名>博客</商品名>
<产地>加点糖博客</产地>
<售价>199</售价>
</商品>
</货架>
package com.addsomesugar;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
public class xmlparser {
@Test
public void domParse() throws Exception {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
Document document = documentBuilder.parse("src/goods.xml");
NodeList nodeList = document.getElementsByTagName("商品名");
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
String content = node.getTextContent();
System.out.println(content);
}
}
@Test
public void domModify() throws Exception {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
Document document = documentBuilder.parse("src/goods.xml");
NodeList nodeList = document.getElementsByTagName("售价");
Node node = nodeList.item(1);
node.setTextContent("99.00");
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
Source xmlSource = new DOMSource(document);
Result outputTarget = new StreamResult("src/goods.xml");
transformer.transform(xmlSource, outputTarget);
}
}
SAX解析
SAX的解析方式:边读边解析。
SAX解析方式的优点:节省内存。
DOM解析方式的缺点:无法修改xml文件。
【注意】:SAX解析会从头到尾全部解析完,不会只解析一部分。
依旧对上文的xml文件进行解析,java代码如下:
package com.addsomesugar;
import org.junit.Test;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.util.ArrayList;
public class xmlparser {
// 遍历每个元素
@Test
public void saxParser() throws Exception {
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
SAXParser saxParser = parserFactory.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
xmlReader.setContentHandler(new Myhandler());
xmlReader.parse("src/goods.xml");
}
// 实现一个自己的文档处理句柄
private class Myhandler extends DefaultHandler {
@Override
public void startDocument() throws SAXException {
System.out.println("文档开始");
}
@Override
public void endDocument() throws SAXException {
System.out.println("文档结束");
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
System.out.println("元素开始<" + qName + ">");
}
@Override
public void endElement(String uri, String localName, String qName) {
System.out.println("元素结束</" + qName + ">");
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String text = new String(ch, start, length);
System.out.println("文本:" + text);
}
}
// 获取特定的元素
@Test
public void saxParser2() throws Exception {
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
SAXParser saxParser = parserFactory.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
// 打印第二个售价
xmlReader.setContentHandler(new DefaultHandler(){
boolean isPrice = false;
int count = 0;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if("售价".equals(qName)) {
isPrice = true;
count ++;
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if("售价".equals(qName)) {
isPrice = false;
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if(isPrice && count == 2) {
String text = new String(ch, start, length);
System.out.println("文本:" + text);
}
}
});
xmlReader.parse("src/goods.xml");
}
// 将xml文件中的内容读到ArrayList中
@Test
public void saxParse2list() throws Exception {
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
SAXParser saxParser = parserFactory.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
xmlReader.setContentHandler(new DefaultHandler(){
ArrayList<Goods> goodsList = null;
Goods goods = null;
String tag = null;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if ("货架".equals(qName)) {
goodsList = new ArrayList<>();
}else if("商品".equals(qName)) {
goods = new Goods();
}else if("商品名".equals(qName)) {
tag = "商品名";
}else if ("产地".equals(qName)) {
tag = "产地";
}else if ("售价".equals(qName)) {
tag = "售价";
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
tag = null; // 保证后面的characters方法不会把回车符读进去
if ("货架".equals(qName)) {
for (Goods g : goodsList) {
System.out.println(g);
}
}else if("商品".equals(qName)) {
goodsList.add(goods);
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if("商品名".equals(tag)) {
goods.setName(new String(ch,start,length));
}else if ("产地".equals(tag)) {
goods.setArea(new String(ch,start,length));
}else if ("售价".equals(tag)) {
goods.setPrice(new String(ch,start,length));
}
}
});
xmlReader.parse("src/goods.xml");
}
}
class Goods {
private String name;
private String area;
private String price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
@Override
public String toString() {
return "Goods{" +
"name='" + name + '\'' +
", area='" + area + '\'' +
", price='" + price + '\'' +
'}';
}
}
pull解析
pull解析与sax解析十分类似,不同的是,pull解析不会自动全部解析,可以只解析一部分,通过手动调用next方法完成解析。pull解析依赖的jar包,jdk中没有包含,需手动添加kxml2-2.3.0.jar
和xmlpull-1.1.3.1.jar
两个jar包。但是在Android中,默认使用pull解析。
package com.addsomesugar;
import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
public class xmlParser {
@Test
public void pullParse() throws Exception {
ArrayList<Goods> goodsList = null;
Goods goods = null;
XmlPullParserFactory parserFactory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = parserFactory.newPullParser();
xmlPullParser.setInput(new FileInputStream(new File("src/goods.xml")), "utf-8");
// 获取当前事件类型
int eventType = xmlPullParser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG:
if("货架".equals(xmlPullParser.getName())) {
goodsList = new ArrayList<>();
}else if("商品".equals(xmlPullParser.getName())) {
goods = new Goods();
}else if("商品名".equals(xmlPullParser.getName())) {
assert goods != null;
goods.setName(xmlPullParser.nextText());
}else if("产地".equals(xmlPullParser.getName())) {
assert goods != null;
goods.setArea(xmlPullParser.nextText());
}else if("售价".equals(xmlPullParser.getName())){
assert goods != null;
goods.setPrice(xmlPullParser.nextText());
}
break;
case XmlPullParser.END_TAG:
if("商品".equals(xmlPullParser.getName())) {
assert goodsList != null;
goodsList.add(goods);
}
break;
default:
break;
}
eventType = xmlPullParser.next();
}
assert goodsList != null;
for (Goods g : goodsList) {
System.out.println(g);
}
}
}
class Goods {
private String name;
private String area;
private String price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
@Override
public String toString() {
return "Goods{" +
"name='" + name + '\'' +
", area='" + area + '\'' +
", price='" + price + '\'' +
'}';
}
}