Java验证解析XML

上一篇讲了Java解析XML,然而如果仅仅按照这种方法来操作,会发现需要大量冗长的编程和错误检查工作。不但需要处理元素间的空白字符,还要检查该文档包含的节点是否和你预期一样。所以在解析XML之前需要验证XML文档的正确性。

场景

例如,读入下面这个XML文档,请看Java XML文档解析

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>  
    <book id="1">  
        <name>Java 核心技术</name>  
        <author>Cornell </author>  
        <year>2014</year>  
        <price>89</price>  
    </book>  
    <book id="2">  
        <name>深入浅出MyBatis</name>  
        <author>杨开振</author>  
        <year>2016</year>  
        <price>69</price>  
    </book>  
    <book id="3">  
        <name>Java RESTful Web Service实战</name>  
        <author>韩陆</author>  
        <year>2016</year>  
        <price>59</price>
    </book>  
</bookstore>

如果不进行XML验证,会出现如下情况

  • 解析的时候需要判断两个节点中的空白字符,如果是空白字符就不读取,不是就读取。
  • 同时如果多写了一个属性<category>computer</category>到book节点。如果没有验证,那么解析的时候就会将category作为一个节点读入,这可能会导致错误。
    <book id="3">  
        <name>Java RESTful Web Service实战</name>  
        <author>韩陆</author>  
        <year>2016</year>  
        <price>59</price>
        <category>computer</category>
    </book>  
  • 如果没对book id进行限制,那么可能存在两个book id相等,这个是不允许发生的

XML解析器

XML解析器最大好处就是它能自动检验某个文档是否具有正确的结构。可以提供文档类型定义(DTD)或一个XML Schema定义。DTD包含用于解释文档如何构成的规则,这些规则指定每个元素的合法子元素和属性。

DTD

-book.dtd 用来描述上述XML的构成的规则,DTD语法规则,参考链接

<?xml version="1.0" encoding="UTF-8"?>
<!--bookstore元素,包括多个book子元素 -->
<!ELEMENT bookstore (book)*>
<!--book元素,包括name,author,year,price子元素-->
<!ELEMENT book (name,author,year,price)>
<!--book元素,有一个属性名为id的属性,这个属性是必须的 -->
<!ATTLIST book id ID #REQUIRED>
<!--name元素,元素只包括文本 -->
<!ELEMENT name (#PCDATA)>
<!--author元素,元素只包括文本 -->
<!ELEMENT author (#PCDATA)>
<!--author元素,元素只包括文本 -->
<!ELEMENT year (#PCDATA)>
<!--price元素,元素只包括文本 -->
<!ELEMENT price (#PCDATA)>

将上述book.dtd纳入入到xml文件中。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE bookstore SYSTEM "book.dtd">
<bookstore>  
    <book id="1">  
        <name>Java 核心技术</name>  
        <author>Cornell </author>  
        <year>2014</year>  
        <price>89</price>  
    </book>  
    <book id="2">  
        <name>深入浅出MyBatis</name>  
        <author>杨开振</author>  
        <year>2016</year>  
        <price>69</price>  
    </book>  
    <book id="3">  
        <name>Java RESTful Web Service实战</name>  
        <author>韩陆</author>  
        <year>2016</year>  
        <price>59</price>
    </book>  
</bookstore>

在DOM解析时候可以通过下列代码来进行验证

    dBuilderFactory = DocumentBuilderFactory.newInstance();
    //解析的时候开启验证
    dBuilderFactory.setValidating(true);
    //忽略节点之间的空白字符
    dBuilderFactory.setIgnoringElementContentWhitespace(true);
    dBuilder = dBuilderFactory.newDocumentBuilder();

整体代码如下

public class ReadXMLByDOMWithValidating {
    private static DocumentBuilderFactory dBuilderFactory = null;
    private static DocumentBuilder dBuilder = null;
    static {
        try {
            /**
             * 要读入一个XML文档,首先要有一个DocumentBuilder对象 可以从DocumentBuilderFactory中得到这个对象
             */
            dBuilderFactory = DocumentBuilderFactory.newInstance();
            dBuilderFactory.setValidating(true);
            dBuilderFactory.setIgnoringElementContentWhitespace(true);
            dBuilder = dBuilderFactory.newDocumentBuilder();
            dBuilder.setErrorHandler(new ErrorHandler() {

                public void warning(SAXParseException exception) throws SAXException {
                    throw exception;

                }

                public void fatalError(SAXParseException exception) throws SAXException {
                    // TODO Auto-generated method stub
                    throw exception;

                }

                public void error(SAXParseException exception) throws SAXException {
                    // TODO Auto-generated method stub
                    throw exception;
                }
            });
        } catch (ParserConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static List<Book> listBooks(String filename) throws SAXException, IOException {
        List<Book> books = new ArrayList<Book>();
        // 可通过DocumentBuilder对象的parse()方法读入整个文档
        Document document = dBuilder.parse(filename);
        // 获得根节点,books.xml对应的就是bookstore节点
        Element root = document.getDocumentElement();
        // 输出根节点的名字,bookstore
        System.out.println(root.getTagName());
        // 获得所有的book节点
        NodeList children = root.getChildNodes();
        // 循环遍历各个book节点
        for (int i = 0; i < children.getLength(); i++) {
            // 获得第i个book节点
            Node child = children.item(i);
            // 用来存储第i个节点的内容
            List<String> bookAttrbuteContent = new ArrayList<String>();
            Book book = new Book();
            /**
             * 这里要注意的是dom会把两个节点之间的空白字符也当做节点 要判断是否是子元素, 而不是空白字符,这个可以参照 《Java核心技术卷
             * 二》的解析XML文档章节,有详细的解释
             */
            Element childElement = (Element) child;
            int bookId = Integer.parseInt(childElement.getAttribute("id").replace("book", ""));
            System.out.println(bookId);
            book.setId(bookId);
            NodeList bookAttrbuteList = childElement.getChildNodes();
            // 循环遍历book节点的各个子节点,如name,author...
            for (int j = 0; j < bookAttrbuteList.getLength(); j++) {
                Node bookAttrbute = bookAttrbuteList.item(j);
                String content = bookAttrbute.getTextContent().trim();
                System.out.println(((Element) bookAttrbute).getTagName() + ":" + content);
                bookAttrbuteContent.add(content);
            }
            book.setName(bookAttrbuteContent.get(0));
            book.setAuthor(bookAttrbuteContent.get(1));
            book.setYear(Integer.parseInt(bookAttrbuteContent.get(2)));
            book.setPrice(Integer.parseInt(bookAttrbuteContent.get(3)));
            books.add(book);
        }
        return books;
    }

    public static void main(String args[]) {
        String fileName = "./src/main/java/com/gethin/xmlparser/bookstore.xml";
        try {
            List<Book> books = ReadXMLByDOMWithValidating.listBooks(fileName);
            for (Book book : books) {
                System.out.println(book);
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

源代码的github链接

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容

  • 1. XML简介 以下内容来自于http://www.w3school.com.cn/xml 基本知识 XML 和...
    WebSSO阅读 1,907评论 1 7
  • 1. XML总结 1.1. XML简介 XML : 可扩展的标记语言。(和HTML非常类似的) 可扩展的。 自定义...
    Ethan_Walker阅读 3,003评论 0 12
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • 这里主要是讲解一下Arduino的程序,使用了Arduino的编程语言,然后简单的写了一个求和函数,如果你对数学的...
    wayen_gogogo阅读 2,086评论 0 1
  • 泥土的清香 草木的芳香 食堂的菜香 女人的体香 金钱的铜香 再香香不过 家里的馨香
    谢谢噢阅读 115评论 0 1