为了账号安全,请及时绑定邮箱和手机立即绑定

学XML看这一篇就够了(1)

标签:
Java
什么是XML?

XML:extensiable markup language 被称作可扩展标记语言

XML简单的历史介绍:

  • gml->sgml->html->xml
  • gml(通用标记语言)–在不同的机器进行通信的数据规范
  • sgml(标准通用标记语言)
  • html(超文本标记语言)
为什么我们需要使用XML呢?
  • ①我们没有XML这种语言之前,我们使用的是String作为两个程序之间的通讯!现在问题就来了,如果我们传输的是带有关系型结构的数据,String怎么表达呢?String对关系型数据不擅长,要是描述起来也难免会有歧义的时候!关系型数据如图下所示:

  • HTML语言本身就有缺陷
    • 标记都是固定的,不能自定义。HTML语言中有什么标记就只能用什么标记
    • HTML标签本身就缺少含义(tr标签里面什么内容都能放进去,不规范!!)
    • HTML没有实现真正的国际化

XML文件就解决了以上的问题了,如果使用XML描述上述图片的关系,是非常简单的!


    <?xml version="1.0" encoding="UTF-8" ?>
    <中国>
        <北京>
            <海淀></海淀>
            <丰台></丰台>
        </北京>
        <湖南>
            <长沙></长沙>
            <岳阳></岳阳>
        </湖南>
        <湖北>
            <武汉></武汉>
            <荆州></荆州>
        </湖北>
    </中国>

XML文件还能使用浏览器打开:

我们可以发现XML是可以描述很复杂的数据关系的


XML的用途

①:配置文件(例子:Tomcat的web.xml,server.xml......),XML能够非常清晰描述出程序之间的关系

②:程序间数据的传输,XML的格式是通用的,能够减少交换数据时的复杂性!

③:充当小型数据库,如果我们的数据有时候需要人工配置的,那么XML充当小型的数据库是个不错的选择,程序直接读取XML文件显然要比读取数据库要快呢!


XML的技术架构

XML被设计为“什么都不做”,XML数据或XML文档只用于组织、存储数据,除此之外的数据生成、读取、传送、存取等等操作都与XML本身无关!

于是乎,想要操作XML,就需要用到XML之外的技术了

  • 为XML定规则:现在一般使用DTD或Schema技术,当然了Schema技术更为先进!
  • 解析XML的数据:一般使用DOM或者SAX技术,各有各的优点
  • 提供样式:XML一般用来存储数据的,但设计者野心很大,也想用来显示数据(但没人用XML来显示数据),就有了XSLT(eXtensiable Stylesheet Language Transformation)可扩展样式转换语言

XML语法:
文档声明:
  • XML声明放在XML的第一行
  • version----版本
  • encoding--编码
  • standalone--独立使用--默认是no。standalone表示该xml是不是独立的,如果是yes,则表示这个XML文档时独立的,不能引用外部的DTD规范文件;如果是no,则该XML文档不是独立的,表示可以引用外部的DTD规范文档。

  • 正确的文档声明格式,属性的位置不能改变!

    <?xml version="1.0" encoding="utf-8" standalone="no"?>

元素

首先在这里说明一个概念:在XML中元素和标签指的是同一个东西!不要被不同的名称所迷惑了!

元素中需要值得注意的地方

  • XML元素中的出现的空格和换行都会被当做元素内容进行处理
  • 每个XML文档必须有且只有一个根元素
  • 元素必须闭合
  • 大小写敏感
  • 不能交叉嵌套
  • 不能以数字开头

看起来好像有很多需要值得注意的地方,其实只需要记住:XML的语法是规范的!不要随意乱写!


属性

属性是作为XML元素中的一部分的,命名规范也是和XML元素一样的!


    <!--属性名是name,属性值是china-->
    <中国 name="china">

    </中国>

注释

注释和HTML的注释是一样的


    <!---->
CDATA

在编写XML文件时,有些内容可能不想让解析引擎解析执行,而是当作原始内容处理。遇到此种情况,可以把这些内容放在CDATA区里,对于CDATA区域内的内容,XML解析程序不会处理,而是直接原封不动的输出

语法:


    <![CDATA[
        ...内容
    ]]>

转义字符

对于一些单个字符,若想显示其原始样式,也可以使用转义的形式予以处理。

处理指令

处理指令,简称PI (processing instruction)。处理指令用来指挥解析引擎如何解析XML文档内容。

例如:

在XML文档中可以使用xml-stylesheet指令,通知XML解析引擎,应用css文件显示xml文档内容。

    <?xml-stylesheet type="text/css" href="1.css"?>
  • XML代码:

    <?xml version="1.0" encoding="UTF-8" ?>
    <?xml-stylesheet type="text/css" href="1.css"?>

    <china>
        <guangzhou>
            广州
        </guangzhou>
        <shenzhen>
            深圳
        </shenzhen>
    </china>
  • CSS代码:

    guangzhou{
        font-size: 40px;
    }
  • 效果:


JDK中的XML API

①:JAXP(The Java API For XML Processing):主要负责解析XML

②:JAXB(Java Architecture for XML Binding):主要负责将XML映射为Java对象

什么是XML解析

前面XML章节已经说了,XML被设计为“什么都不做”,XML只用于组织、存储数据,除此之外的数据生成、读取、传送等等的操作都与XML本身无关!

XML解析就是读取XML的数据!


XML解析方式

XML解析方式分为两种:

①:dom(Document Object Model)文档对象模型,是W3C组织推荐解析XML的一种方式

②:sax(Simple API For XML),它是XML社区的标准,几乎所有XML解析器都支持它!

XML解析操作

从上面的图很容易发现,应用程序不是直接对XML文档进行操作的,而是由XML解析器对XML文档进行分析,然后应用程序通过XML解析器所提供的DOM接口或者SAX接口对分析结果进行操作,从而间接地实现了对XML文档的访问!

常用的解析器和解析开发包的关系如下所示


为什么有3种开发包?
  • jaxp开发包是JDK自带的,不需要导入开发包。
  • 由于sun公司的jaxp不够完善,于是就被研发了Jdom。XML解析如果使用Jdom,需要导入开发包
  • dom4j是由于Jdom的开发人员出现了分歧,dom4j由Jdom的一批开发人员所研发。XML解析如果使用Jdom,需要导入开发包【现在用dom4j是最多的!】

jaxp

虽然jaxp解析XML的性能以及开发的简易度是没有dom4j好,但是jaxp不管怎么说都是JDK内置的开发包,我们是需要学习的

DOM解析操作

DOM解析是一个基于对象的API,它把XML的内容加载到内存中,生成与XML文档内容对应的模型!当解析完成,内存中会生成与XML文档的结构与之对应的DOM对象树,这样就能够根据树的结构,以节点的形式对文档进行操作!

简单来说:DOM解析会把XML文档加载到内存中,生成DOM树的元素都是以对象的形式存在的!我们操作这些对象就能够操作XML文档了!

  • 下面这样图就能很好地说明了,是怎么样生成与XML文档内容对应的DOM树!


既然XML文档的数据是带有关系型的,那么生成的DOM树的节点也是有关系的:

  • 位于一个节点之上的节点是该节点的父节点(parent)
  • 一个节点之下的节点是该节点的子节点(children)
  • 同一层次,具有相同父节点的节点是兄弟节点(sibling)
  • 一个节点的下一个层次的节点集合是节点后代(descendant)
  • 父、祖父节点及所有位于节点上面的,都是节点的祖先(ancestor)

在DOM解析中有几个核心的操作接口:

  • Document【代表整个XML文档,通过Document节点可以访问XML文件中所有的元素内容!】
  • Node【Node节点几乎在XML操作接口中几乎相当于普通Java类的Object,很多核心接口都实现了它,在下面的关系图可以看出!】
  • NodeList【代表着一个节点的集合,通常是一个节点中子节点的集合!】
  • NameNodeMap【表示一组节点和其唯一名称对应的一一对应关系,主要用于属性节点的表示(书上说是核心的操作接口,但我好像没用到!呃呃呃,等我用到了,我再来填坑!)】

节点之间的关系图:

  • 有人可能会很难理解,为什么Document接口比Node接口还小,呃呃呃,我是这样想的:一个Document由无数个Node组成,这样我也能把Document当成是Node呀!如果实在想不通:人家都这样设计了,你有种就不用啊!!(开玩笑的.....)

好的,不跟你们多bb,我们来使用一下Dom的方式解析XML文档吧!

  • XML文档代码

    <?xml version="1.0" encoding="UTF-8" ?>
    <china>
        <guangzhou >广州</guangzhou>
        <shenzhen>深圳</shenzhen>
        <beijing>北京</beijing>
        <shanghai>上海</shanghai>
    </china>
  • 根据XML解析的流程图,我们先要获取到解析器对象!

    public class DomParse {

        public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {

            //API规范:需要用一个工厂来造解析器对象,于是我先造了一个工厂!
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

            //获取解析器对象
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

            //获取到解析XML文档的流对象
            InputStream inputStream = DomParse.class.getClassLoader().getResourceAsStream("city.xml");

            //解析XML文档,得到了代表XML文档的Document对象!
            Document document = documentBuilder.parse(inputStream);

        }
    }
  • 解析XML文档的内容用来干嘛?无非就是增删改查遍历,只要我们会对XML进行增删改查,那就说明我们是会使用DOM解析的

遍历

  • 我们再来看一下XML文档的内容,如果我们要遍历该怎么做?

  • 可能我们会有两种想法

    • ①:从XML文档内容的上往下看,看到什么就输出什么!【这正是SAX解析的做法】
    • ②:把XML文档的内容分成两部分,一部分是有子节点的,一部分是没有子节点的(也就是元素节点!)。首先我们判断是否为元素节点,如果是元素节点就输出,不是元素节点就获取到子节点的集合,再判断子节点集合中的是否是元素节点,如果是元素节点就输出,如果不是元素节点获取到该子节点的集合....好的,一不小心就递归了...
  • 我们来对XML文档遍历一下吧,为了更好地重用,就将它写成一个方法吧(也是能够更好地用递归实现功能)

    public class DomParse {

        public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {

            //API规范:需要用一个工厂来造解析器对象,于是我先造了一个工厂!
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

            //获取解析器对象
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

            //获取到解析XML文档的File对象
            InputStream inputStream = DomParse.class.getClassLoader().getResourceAsStream("city.xml");

            //解析XML文档,得到了代表XML文档的Document对象!
            Document document = documentBuilder.parse(inputStream);

            //把代表XML文档的document对象传递进去给list方法
            list(document);

        }

        //我们这里就接收Node类型的实例对象吧!多态!!!
        private static void list(Node node) {

            //判断是否是元素节点,如果是元素节点就直接输出
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                System.out.println(node.getNodeName());
            }

            //....如果没有进入if语句,下面的肯定就不是元素节点了,所以获取到子节点集合
            NodeList nodeList = node.getChildNodes();

            //遍历子节点集合
            for (int i = 0; i < nodeList.getLength(); i++) {

                //获取到其中的一个子节点
                Node child = nodeList.item(i);

                //...判断该子节点是否为元素节点,如果是元素节点就输出,不是元素节点就再获取到它的子节点集合...递归了

                list(child);
            }

        }
    }
  • 效果:


查询

现在我要做的就是:读取guangzhou这个节点的文本内容!


    private static void read(Document document) {

        //获取到所有名称为guangzhou节点
        NodeList nodeList = document.getElementsByTagName("guangzhou");

        //取出第一个名称为guangzhou的节点
        Node node = nodeList.item(0);

        //获取到节点的文本内容
        String value = node.getTextContent();

        System.out.println(value);

    }
  • 效果:


增加

现在我想多增加一个城市节点(杭州),我需要这样做:


    private static void add(Document document) {

        //创建需要增加的节点
        Element element = document.createElement("hangzhou");

        //向节点添加文本内容
        element.setTextContent("杭州");

        //得到需要添加节点的父节点
        Node parent = document.getElementsByTagName("china").item(0);

        //把需要增加的节点挂在父节点下面去
        parent.appendChild(element);

    }
  • 做到这里,我仅仅在内存的Dom树下添加了一个节点,要想把内存中的Dom树写到硬盘文件中,需要转换器

  • 获取转换器也十分简单

        //获取一个转换器它需要工厂来造,那么我就造一个工厂
        TransformerFactory transformerFactory = TransformerFactory.newInstance();

        //获取转换器对象
        Transformer transformer = transformerFactory.newTransformer();
  • 把内存中的Dom树更新到硬盘文件中的transform()方法就稍稍有些复杂了

  • 它需要一个Source实例对象和Result的实例对象,这两个接口到底是什么玩意啊?

  • 于是乎,我就去查API,发现DomSource实现了Source接口,我们使用的不正是Dom解析吗,再看看构造方法,感觉就是它了!

  • 而SteamResult实现了Result接口,有人也会想,DomResult也实现了Result接口啊,为什么不用DomResult呢?我们现在做的是把内存中的Dom树更新到硬盘文件中呀,当然用的是StreamResult啦!

  • 完整代码如下:

    private static void add(Document document) throws TransformerException {

        //创建需要增加的节点
        Element element = document.createElement("hangzhou");

        //向节点添加文本内容
        element.setTextContent("杭州");

        //得到需要添加节点的父节点
        Node parent = document.getElementsByTagName("china").item(0);

        //把需要增加的节点挂在父节点下面去
        parent.appendChild(element);

        //获取一个转换器它需要工厂来造,那么我就造一个工厂
        TransformerFactory transformerFactory = TransformerFactory.newInstance();

        //获取转换器对象
        Transformer transformer = transformerFactory.newTransformer();

        //把内存中的Dom树更新到硬盘中
        transformer.transform(new DOMSource(document),new StreamResult("city.xml"));
    }
  • 效果:


刚刚增加的节点是在china节点的末尾处的,现在我想指定增加节点的在beijing节点之前,是这样做的:


    private static void add2(Document document) throws TransformerException {

        //获取到beijing节点
        Node beijing = document.getElementsByTagName("beijing").item(0);

        //创建新的节点
        Element element = document.createElement("guangxi");

        //设置节点的文本内容
        element.setTextContent("广西");

        //获取到要创建节点的父节点,
        Node parent = document.getElementsByTagName("china").item(0);

        //将guangxi节点插入到beijing节点之前!
        parent.insertBefore(element, beijing);

        //将内存中的Dom树更新到硬盘文件中
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult("city.xml"));

    }
  • 效果:


点击查看更多内容
2人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
1.5万
获赞与收藏
6488

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消