Java实现XML文件的校验

XML是常用的数据交换格式和配置文件格式,本文主要讲解对XML文件的解析与校验方法。
我们先来看个例子:

book.xml

<bookstore>
    <book id="1">
        <name>冰与火之歌</name>
        <author>乔治马丁</author>
        <year>2014</year>
        <price>
            89.00
            <!--</price><author>余欢</author><price>0.12-->
        </price>
    </book>
    <book id="2">
        <name>安徒生童话</name>
        <author>安徒生</author>
        <year>2004</year>
        <price>77.50</price>
    </book>
    <book id="3">
        <name>think think think</name>
        <author>aaa</author>
        <year>1997</year>
        <price>100.00</price>
    </book>
</bookstore>

解析xml:

public List<Book> getBooks(File file) {
        List<Book> bookList = new ArrayList<>();
        Book book;

        SAXReader reader = new SAXReader();
        try {
            Document document = reader.read(file);
            Element bookstore = document.getRootElement();
            Iterator storeit = bookstore.elementIterator();

            while(storeit.hasNext()){
                book = new Book();
                Element bookElement = (Element) storeit.next();
                //遍历bookElement的属性
                List<Attribute> attributes = bookElement.attributes();
                for(Attribute attribute : attributes){
                    if(attribute.getName().equals("id")){
                        String id = attribute.getValue();//System.out.println(id);
                        book.setId(Integer.parseInt(id));
                    }
                }

                Iterator bookit = bookElement.elementIterator();
                while(bookit.hasNext()){
                    Element child = (Element) bookit.next();
                    String nodeName = child.getName();
                    if(nodeName.equals("name")){
                        //System.out.println(child.getStringValue());
                        String name = child.getStringValue();
                        book.setName(name);
                    }else if(nodeName.equals("author")){
                        String author = child.getStringValue();
                        book.setAuthor(author);
                    }else if(nodeName.equals("year")){
                        String year = child.getStringValue();
                        book.setYear(Integer.parseInt(year));
                    }else if(nodeName.equals("price")){
                        String price = child.getStringValue();
                        book.setPrice(Double.parseDouble(price));
                    }
                }
                bookList.add(book);
                book = null;
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return bookList;
    }

如上是一个书籍清单的xml文件,程序解析这个xml文件就可以得到书籍列表,里面包含书籍的名称、作者、年份和价格:

Book{id=1, name='冰与火之歌', author='乔治马丁', year=2014, price=89.0}
Book{id=2, name='安徒生童话', author='安徒生', year=2004, price=77.5}
Book{id=3, name='think think think', author='aaa', year=1997, price=100.0}

如果有恶意攻击者将上述book.xml文件修改为下面的文件:

<bookstore>
    <book id="1">
        <name>冰与火之歌</name>
        <author>乔治马丁</author>
        <year>2014</year>
        <price>
            89.00
            </price><author>余欢</author><price>0.12
        </price>
    </book>
    <book id="2">
        <name>安徒生童话</name>
        <author>安徒生</author>
        <year>2004</year>
        <price>77.50</price>
    </book>
    <book id="3">
        <name>think think think</name>
        <author>aaa</author>
        <year>1997</year>
        <price>100.00</price>
    </book>
</bookstore>

将会导致解析出来的数据有错误:

Book{id=1, name='冰与火之歌', author='余欢', year=2014, price=0.12}
Book{id=2, name='安徒生童话', author='安徒生', year=2004, price=77.5}
Book{id=3, name='think think think', author='aaa', year=1997, price=100.0}

XML文件是一种常用的配置文件和数据交换格式,它是一种标签化的数据格式,可以作为Web Service服务的传输数据,因此对于XML文件的校验是有必要的。

XML本地校验

本地校验大致有两种方式,分别为编写xml的DTD和XSD验证文件。

DTD验证

关于DTD的语法可参考如下两个网站:

https://www.runoob.com/dtd/dtd-tutorial.html

http://www.w3school.com.cn/dtd/index.asp

本文重在讲解校验XML的方法,故具体语法规则不在此处赘述。直接给出针对上述book.xml的dtd验证文件:

book.dtd

<!ELEMENT bookstore (book)*>
<!ELEMENT book (name, author, year, price)>
        <!ATTLIST book id ID #IMPLIED>
<!ELEMENT name (#PCDATA)>
<!ELEMENT author (#PCDATA)>
<!ELEMENT year ()>
<!ELEMENT price (#PCDATA)>

在book.xml头部引入该校验文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE bookstore SYSTEM "http://www.w3school.com.cn/dtd/book.dtd">
<bookstore>
    <book id="1">
        <name>冰与火之歌</name>
        <author>乔治马丁</author>
        <year>2014</year>
        <price>
            89.00
            </price><author>余欢</author><price>0.12
        </price>
    </book>
    <book id="2">
        <name>安徒生童话</name>
        <author>安徒生</author>
        <year>2004</year>
        <price>77.50</price>
    </book>
    <book id="3">
        <name>think think think</name>
        <author>aaa</author>
        <year>1997</year>
        <price>100.00</price>
    </book>
</bookstore>

dtd文件作用:

通过 DTD,您的每一个 XML 文件均可携带一个有关其自身格式的描述。

通过 DTD,独立的团体可一致地使用某个标准的 DTD 来交换数据。

而您的应用程序也可使用某个标准的 DTD 来验证从外部接收到的数据。

您还可以使用 DTD 来验证您自身的数据。

dtd文件只能起到初步校验作用,要想细致地验证xml文件还得用xsd。

XSD验证

关于xsd的语法规则请查询下面网站:

http://www.w3school.com.cn/schema/index.asp

book.xsd

<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://www.w3school.com.cn"
           xmlns="http://www.w3school.com.cn"
           elementFormDefault="qualified">
    <xs:element name="bookstore">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="book" minOccurs="0" maxOccurs="unbounded">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="name" type="xs:string"/>
                            <xs:element name="author" type="xs:string"/>
                            <xs:element name="year">
                                <xs:simpleType>
                                    <xs:restriction base="xs:integer">
                                        <xs:minInclusive value="0"/>
                                        <xs:maxInclusive value="3000"/>
                                    </xs:restriction>
                                </xs:simpleType>
                            </xs:element>
                            <xs:element name="price">
                                <xs:simpleType>
                                    <xs:restriction base="xs:decimal">
                                        <xs:pattern value="[\d]{0,16}\.[\d]{2}"/>
                                    </xs:restriction>
                                </xs:simpleType>
                            </xs:element>
                        </xs:sequence>

                        <xs:attribute type="xs:integer" name="id"/>

                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

再在book.xml中引入该xsd文件:

<?xml version="1.0" encoding="UTF-8"?>
<bookstore xmlns="http://www.w3school.com.cn"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.w3school.com.cn book.xsd">
    <book id="1">
        <name>冰与火之歌</name>
        <author>乔治马丁</author>
        <year>2014</year>
        <price>
            89.00
            </price><author>余欢</author><price>0.12
        </price>
    </book>
    <book id="2">
        <name>安徒生童话</name>
        <author>安徒生</author>
        <year>2004</year>
        <price>77.50</price>
    </book>
    <book id="3">
        <name>think think think</name>
        <author>aaa</author>
        <year>1997</year>
        <price>100.00</price>
    </book>
</bookstore>

这时,如果本地xml文件不符合制定好的xsd规则,本地xml文件就会报语法错误,从而提前将错误暴露出来,减小代价。这时候有人会说,如果是接收别人传过来的xml文件怎么去校验呢?请接着看下面的文章:

JAVA校验XML

利用本地编辑好的dtd和xsd文件都可以拿来校验xml文件,本文以xsd校验为例来说明:

validateXMLByXSD.java

   public String validateXMLByXSD(String xmlFileName, String xsdFileName) {
        String result = "";
        try {
            //创建默认的XML错误处理器
            XMLErrorHandler errorHandler = new XMLErrorHandler();
            //获取基于 SAX 的解析器的实例
            SAXParserFactory factory = SAXParserFactory.newInstance();
            //解析器在解析时验证 XML 内容。
            factory.setValidating(true);
            //指定由此代码生成的解析器将提供对 XML 名称空间的支持。
            factory.setNamespaceAware(true);
            //使用当前配置的工厂参数创建 SAXParser 的一个新实例。
            SAXParser parser = factory.newSAXParser();
            //创建一个读取工具
            SAXReader xmlReader = new SAXReader();
            //获取要校验xml文档实例
            Document xmlDocument = (Document) xmlReader.read(new File(xmlFileName));
            //设置 XMLReader 的基础实现中的特定属性。核心功能和属性列表可以在 [url]http://sax.sourceforge.net/?selected=get-set[/url] 中找到。
            parser.setProperty(
                    "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
                    "http://www.w3.org/2001/XMLSchema");
            parser.setProperty(
                    "http://java.sun.com/xml/jaxp/properties/schemaSource",
                    "file:" + xsdFileName);
            //创建一个SAXValidator校验工具,并设置校验工具的属性
            SAXValidator validator = new SAXValidator(parser.getXMLReader());
            //设置校验工具的错误处理器,当发生错误时,可以从处理器对象中得到错误信息。
            validator.setErrorHandler(errorHandler);
            //校验
            validator.validate(xmlDocument);

            XMLWriter writer = new XMLWriter(OutputFormat.createPrettyPrint());
            //如果错误信息不为空,说明校验失败,打印错误信息
            if (errorHandler.getErrors().hasContent()) {
                System.out.println("XML文件通过XSD文件校验失败!");
                writer.write(errorHandler.getErrors());
                result = "XML文件通过XSD文件校验失败!";
            } else {
                System.out.println("Good! XML文件通过XSD文件校验成功!");
                result = "Good! XML文件通过XSD文件校验成功!";
            }
        } catch (Exception ex) {
            result = "XML文件通过XSD文件校验失败!原因:"+ex.getMessage();
            System.out.println("XML文件: " + xmlFileName + " 通过XSD文件:" + xsdFileName + "检验失败。/n原因: " + ex.getMessage());
            ex.printStackTrace();
        }
        return result;
    }

UnitTest:

@Test
    public void validateXml() throws FileNotFoundException {
        String xmlFileName1 = ResourceUtils.getURL("classpath:xml/book.xml").getPath();
        String xmlFileName2 = ResourceUtils.getURL("classpath:xml/book_error.xml").getPath();
        String xsdFileName = ResourceUtils.getURL("classpath:xml/book.xsd").getPath();
        String result1 = xmlService.validateXMLByXSD(xmlFileName1, xsdFileName);
        String result2 = xmlService.validateXMLByXSD(xmlFileName2, xsdFileName);
        System.out.println(result1);
        System.out.println(result2);
    }

如果符合你传入的xsd规则文件,则校验会成功,否则会校验失败。

Good! XML文件通过XSD文件校验成功!
XML文件通过XSD文件校验失败!

<errors>
  <error column="29" line="9" systemID="file:///D:/workspace/demo/target/classes/xml/book_error.xml">cvc-complex-type.2.4.d: 发现了以元素 'author' 开头的无效内容。此处不应含有子元素。</error>
</errors>Good! XML文件通过XSD文件校验成功!
XML文件通过XSD文件校验失败!

当然你还可以写程序实现利用dtd文件来校验xml。

总结

本文主要是讲解XML文件校验的一般方法,主要分为本地校验和用java程序动态去校验xml文件,希望对读者有用!

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

推荐阅读更多精彩内容

  • 一、XML介绍 XML 指可扩展标记语言(EXtensible Markup Language),也是一种标记语言...
    圣贤与无赖阅读 1,112评论 2 3
  • 课程内容:XML 安装MyEclipse开发工具 * 破解(看图) * 配置 * 配置工作空间的编码(UTF-...
    流年划破容颜_cc55阅读 1,171评论 0 2
  • # XML复习 ## 第一章 ## 思考题 **什么是XML?** XML是可扩展性标记语言,XML是标准通用标记...
    冷漠铁锤丁富贵阅读 794评论 0 0
  • 1. XML总结 1.1. XML简介 XML : 可扩展的标记语言。(和HTML非常类似的) 可扩展的。 自定义...
    Ethan_Walker阅读 2,979评论 0 12
  • 红线财务 在汽车领域,有一种说法叫做“把引擎开到红线档”。“红线”就是汽车引擎在不熄火的情况下所能维持的最快速度。...
    fubaba学财商阅读 144评论 0 1