美文网首页
Java实现XML文件的校验

Java实现XML文件的校验

作者: 断翅绝尘 | 来源:发表于2019-10-09 15:48 被阅读0次

    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文件,希望对读者有用!

    相关文章

      网友评论

          本文标题:Java实现XML文件的校验

          本文链接:https://www.haomeiwen.com/subject/pusgpctx.html