XML解析 验证之XSD和DTD验证以及 SpringXML验证源码分析

本文深入探讨XML验证机制,包括DTD和XSD的作用、语法特点及应用。对比DTD与XSD,详细解析XSD如何定义XML文档结构,以及如何通过JAVA代码实现XSD对XML文档的有效性验证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.DTD(Documnet Type Definition)


DTD即文档类型定义,是一种XML约束模式语言,是XML文件的验证机制,属于XML文件组成的一部分。
DTD 是一种保证XML文档格式正确的有效方法,可以通过比较XML文档和DTD文件来看文档是否符合规范,元素和标签使用是否正确。
一个 DTD文档包含:
元素的定义规则,元素间关系的定义规则,元素可使用的属性,可使用的实体或符号规则。 DTD和XSD相比:DTD 是使用非 XML 语法编写的。 DTD 不可扩展,不支持命名空间,只提供非常有限的数据类型 .

2.XSD(XML Schemas Definition) 


XML Schema语言也就是XSD。XML Schema描述了XML文档的结构。 可以用一个指定的XML Schema来验证某个XML文档,以检查该XML文档是否符合其要求。文档设计者可以通过XML Schema指定一个XML文档所允许的结构和内容,并可据此检查一个XML文档是否是有效的。XML Schema本身是一个XML文档,它符合XML语法结构。可以用通用的XML解析器解析它。 一个XML Schema会定义:文档中出现的元素、文档中出现的属性、子元素、子元素的数量、子元素的顺序、元素是否为空、元素和属性的数据类型、元素或属性的默认 和固定值。

XSD是DTD替代者的原因,一是据将来的条件可扩展,二是比DTD丰富和有用,三是用XML书写,四是支持数据类型,五是支持命名空间。

XML Schema的优点:

  1. XML Schema基于XML,没有专门的语法
  2. XML Schema可以象其他XML文件一样解析和处理
  3. XML Schema比DTD提供了更丰富的数据类型.
  4. XML Schema提供可扩充的数据模型。
  5. XML Schema支持综合命名空间
  6. XML Schema支持属性组。

3 XSD验证

XML文件

<?xml version="1.0"?>
<note xmlns="http://adcoup.baidu.com/schema/note">
    <to>Tove</to>
    <from>Jani</from>
    <heading>Reminder</heading>
    <body>Don't forget me this weekend!</body>
</note>

XSD文件

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://adcoup.baidu.com/schema/note" xmlns="http://adcoup.baidu.com/schema/note"
           elementFormDefault="qualified">
    <xs:element name="note">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="to" type="xs:string" />
                <xs:element name="from" type="xs:string" />
                <xs:element name="heading" type="xs:string" />
                <xs:element name="body" type="xs:string" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

JAVA验证代码

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.io.SAXValidator;
import org.dom4j.util.XMLErrorHandler;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.net.URL;
import java.util.List;

public class XsdXmlValidator {

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

    public static void validateXMLByXSD() {
        // 从类路径下读取文件
        URL xmlFileName = XsdXmlValidator.class.getClassLoader().getResource("note.xml");
        // 输出为file:/D:/MyWorkSpace/FirstWorkSpace/validate-xml-test/target/classes/test_schema.xsd
        String xsdFileName = XsdXmlValidator.class.getClassLoader().getResource("note.xsd").toString();

        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(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",xsdFileName);
            //创建一个SAXValidator校验工具,并设置校验工具的属性
            SAXValidator validator = new SAXValidator(parser.getXMLReader());
            //设置校验工具的错误处理器,当发生错误时,可以从处理器对象中得到错误信息。
            validator.setErrorHandler(errorHandler);
            //校验
            validator.validate(xmlDocument);

            StringBuilder errorMsg = new StringBuilder();
            //如果错误信息不为空,说明校验失败,打印错误信息
            if (errorHandler.getErrors().hasContent()) {
                List<Element> elements = errorHandler.getErrors().elements();
                for (Element element : elements) {
                    String line = String.valueOf(Integer.parseInt(element.attributeValue("line")) - 1);
                    String text = element.getText();
                    errorMsg.append("(第 " + line + "行出现错误) " + text+"\r\n");
                }
                errorMsg.append("XML文件通过XSD文件校验失败!");
                System.out.println(errorMsg.toString());
            }else {
                System.out.println("XML文件通过XSD文件校验成功!");
            }
        } catch (Exception ex) {
            System.out.println("XML文件: " + xmlFileName + " 通过XSD文件:" + xsdFileName + "检验失败。/n原因: " + ex.getMessage());
            ex.printStackTrace();
        }
    }

}

4 关于Spring对XML解析的验证源码分析

源码在DefaultDocumentLoader类的createDocumentBuilderFactory()方法:

1  下面使用了javax.xml获取DocumenBuildFactory.这行代码是原生的Java对xml的解析!

 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

2 createDocumentBuilderFactory方法传递了两个参数

1)int validationMode:XML验证模式
   /**
	 * 0表示禁用验证XML
	 */
	public static final int VALIDATION_NONE = 0;

	/**
	 * 1表示根据XML文件格式自动验证
	 */
	public static final int VALIDATION_AUTO = 1;

	/**
	 * DTD方式验证XML
	 */
	public static final int VALIDATION_DTD = 2;

	/**
	 * XSD方式验证XML
	 */
	public static final int VALIDATION_XSD = 3;

boolean namespaceAware 是否设置命名空间。在XML解析过程中依情况而定。我在上述XSD代码验证过程中设置了true
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware);

流程如下:

1 根据DocumentBuilderFactory静态工厂实例化DocumentBuilderFactory对象

2 设置命名空间

3 如果验证模式不等于0,表示需要验证XML,设置验证为true

4 如果等于3,设置验证属性进行验证。验证过程出现异常,直接捕获,并且记录验证失败。

5 返回DocumentBuilderFactory工厂实例对象!

如下的源码我做了注释以及稍微改动!

protected DocumentBuilderFactory createDocumentBuilderFactory
                  (int validationMode, boolean namespaceAware) {

      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      /** namespaceAware 是否需要设置命名空间*/
      factory.setNamespaceAware(namespaceAware);
      /**
       * validationMode=0 不验证
       * validationMode=1 自动验证
       * validationMode=2 DTD验证
       * validationMode=3 XSD验证
       * 从代码可以看出Spring使用XSD验证
       */
      if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
         /** 如果不等于0,表示XML需要验证*/
         factory.setValidating(true);
         /** 等于3,表示使用XSD验证*/
         if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
            // XSD验证,强制使用命名空间,设置为true
            factory.setNamespaceAware(true);
            try {
               /** 设置XML属性 java XML以及w3c
                * SCHEMA_LANGUAGE_ATTRIBUTE=http://java.sun.com/xml/jaxp/properties/schemaLanguage
                * XSD_SCHEMA_LANGUAGE=http://www.w3.org/2001/XMLSchema
                * */
               factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
            }
            catch (IllegalArgumentException ex) {
               System.out.println("验证失败");
            }
         }
      }

      return factory;
   }

 

 

 

 

 

org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [beans.xml]; nested exception is java.io.FileNotFoundException: class path resource [beans.xml] cannot be opened because it does not exist at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:342) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:310) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:224) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:195) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:257) at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:128) at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:94) at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130) at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:671) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85) at com.my.SpringMyBatisTest.findAllPublishersMybatisTest(SpringMyBatisTest.java:1
最新发布
04-28
### Spring BeanDefinitionStoreException 导致 beans.xml 文件未找到的原因分析 在Spring框架中,`BeanDefinitionStoreException` 是一种常见的运行时异常,它通常表示Spring容器在加载解析Bean定义的过程中发生了错误。根据提供的引用内容以及相关背景知识,以下是关于 `beans.xml` 文件未找到的具体原因及其解决方案。 #### 原因分析 1. **文件路径不正确** 如果指定的XML配置文件不在类路径下或者路径拼写有误,则会导致 `FileNotFoundException` 或者类似的异常被抛出。例如,在引用中提到的情况:`class path resource [Spring.xml] cannot be opened because it does not exist` 表明Spring未能定位到该资源文件[^1]。 2. **项目结构问题** 在某些情况下,开发人员可能将XML配置文件放置到了项目的非标准目录中(如 `src/main/resources` 外部)。这可能导致Spring无法自动扫描并加载这些文件。例如,如果 `beans.xml` 被放在了一个自定义目录而未通过适当的方式引入,则会出现此类问题[^2]。 3. **命名冲突或拼写错误** 配置文件名可能存在大小写敏感性差异或其他形式上的拼写失误。即使物理上存在名为 `Beans.xml` 的文件,但如果程序代码中引用的是小写的 `beans.xml`,则仍然会触发找不到文件的异常[^4]。 4. **依赖注入方式不当** 当采用基于注解的方式来管理组件而非完全依靠传统XML方式进行声明式编程时,可能会忽略掉必要的上下文初始化步骤,进而影响整个应用生命周期内的资源配置过程[^5]。 #### 解决方案 针对以上列举的各种可能性,可以采取以下措施逐一排查并解决问题: ##### 方法一:确认文件位置与名称匹配 确保 `beans.xml` 存在于应用程序的classpath根目录之下,默认应该是位于Maven工程下的 `src/main/resources/` 中。可以通过IDE内置功能快速验证实际存在的文件列表是否包含目标项。 ##### 方法二:调整加载逻辑 修改启动脚本或者其他地方设置好的参数值以指向确切有效的地址;如果是硬编码的形式调用了某个特定路径的话,请仔细核对该字符串表达式的准确性。 ```java ApplicationContext context = new ClassPathXmlApplicationContext("correct/path/to/beans.xml"); ``` ##### 方法三:启用自动装配机制减少手动维护成本 考虑迁移到更加现代化的做法——利用@ComponentScan配合@Configuration类完成大部分基础工作流的设计,从而降低对外部独立xml文档需求程度的同时提高灵活性与可扩展性。 ```java @SpringBootApplication(scanBasePackages={"com.example"}) public class Application { public static void main(String[] args){ SpringApplication.run(Application.class,args); } } ``` ##### 方法四:检查schema定义合法性 对于那些坚持保留原有风格的应用而言,务必保证所有的namespace URI都合法有效,并且遵循官方推荐的最佳实践指南编写相应的DTD部分。 ```xml <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> </beans> ``` --- ### 总结 综上所述,要彻底消除由 `BeanDefinitionStoreException` 引发的 `beans.xml` 找不到的问题,需从多个角度出发进行全面诊断,包括但不限于核实源码内部实现细节、优化现有架构设计思路等方面入手加以改进完善。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值