xml语法

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文档必须有且仅有一个根标签。其他标签都是这个根标签的子标签。

元素标签的命名规范:

  1. 区分大小写,和是两个不同的标签。
  2. 不能以下划线(_)开头。
  3. 不能以xml(或XML、Xml等)开头。
  4. 不能包含空格。
  5. 名称中间不能包含冒号(:)

元素内容中,制表符和换行符都会被保留。

属性

一个元素可以包含多个属性,但是属性不允许重复。属性的值需要用引号(单引号或双引号)标出。
例如:

<tag1 name="value">content</tag2>

属性的命名规范与元素标签的命名规范一致。

注释

关于注释的语法是:

<!--这是注释-->
<tag1>content</tag1>

CDATA区

CDATA区段的文本不会被解析器解析,用于传递特殊字符,例如<>等。

<![CDATA[内容]]>

特殊字符

对于特殊字符,必须进行转义:

特殊字符代替符号
&&amp
<&lt
>&gt
"&quot
&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文档。

  1. 当引用的DTD文档在本地时:<!DOCTYPE 根元素 SYSTEM “goods.dtd”>
  2. 当引用的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">
        <商品名>博客</商品名>
        <产地>&copyright;</产地>
        <售价>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.jarxmlpull-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 + '\'' +
                '}';
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值