java验证xml是否规范_Java XML验证API

本文介绍了Java中用于XML验证的API,包括验证方式、抽象工厂、错误处理和模式扩充等内容。通过示例展示了如何使用API进行XHTML和DocBook文档的验证,强调了验证在数据处理中的重要性,并提到了不同架构语言的处理方式。

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

2010年2月10日- 作为读者评论的后续活动,作者对Validation下的最后一段进行了更新,只有模式是线程安全的,而验证程序和模式工厂则不是。

验证是一个强大的工具。 它使您能够快速检查输入内容是否大致符合您的期望,并快速拒绝任何与流程无法处理的文档。 如果数据有问题,最好早发现。

在可扩展标记语言(XML)的上下文中,验证通常涉及使用几种架构语言(例如,万维网联合会(W3C)XML架构语言(XSD),RELAX NG,文档类型)中的文档内容编写详细的规范。定义(DTD)和Schematron。 有时验证是在解析时执行的,有时是在解析之后立即执行的。 但是,通常是在对输入进行任何进一步处理之前完成。 (此描述用粗笔描画-有例外。)

直到最近,程序要求进行验证的确切应用程序编程接口(API)随架构语言和解析器的不同而变化。 通常在XML的简单API(SAX),文档对象模型(DOM)和XML处理的Java™API(JAXP)中将DTD和XSD作为配置选项进行访问。 RELAX NG需要自定义库和API。 Schematron可能会使用XML(TrAX)的Transformations API; 甚至其他模式语言也要求程序员学习更多的API,即使它们执行的基本操作相同。

Java 5引入了javax.xml.validation包,以向验证服务提供独立于模式语言的接口。 当单独安装JAXP 1.3时,此包在Java 1.3和更高版本中也可用。 在其他产品中,该库的实现包含在Xerces 2.8中。

验证方式

javax.xml.validation API使用三个类来验证文档: SchemaFactorySchemaValidator 。 它还广泛使用了TrAX的javax.xml.transform.Source接口来表示XML文档。 简而言之, SchemaFactory会从中读取创建Schema对象的架构文档(通常是XML文件)。 Schema对象创建一个Validator对象。 最后, Validator对象验证表示为Source的XML文档。

清单1显示了一个简单的程序,用于根据DocBook XSD模式验证在命令行上输入的URL。

清单1.验证可扩展超文本标记语言(XHTML)文档
import java.io.*;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.*;
import org.xml.sax.SAXException;

public class DocbookXSDCheck {

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

        // 1. Lookup a factory for the W3C XML Schema language
        SchemaFactory factory = 
            SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
        
        // 2. Compile the schema. 
        // Here the schema is loaded from a java.io.File, but you could use 
        // a java.net.URL or a javax.xml.transform.Source instead.
        File schemaLocation = new File("/opt/xml/docbook/xsd/docbook.xsd");
        Schema schema = factory.newSchema(schemaLocation);
    
        // 3. Get a validator from the schema.
        Validator validator = schema.newValidator();
        
        // 4. Parse the document you want to check.
        Source source = new StreamSource(args[0]);
        
        // 5. Check the document
        try {
            validator.validate(source);
            System.out.println(args[0] + " is valid.");
        }
        catch (SAXException ex) {
            System.out.println(args[0] + " is not valid because ");
            System.out.println(ex.getMessage());
        }  
        
    }

}

这是使用Java 2 Software Development Kit(JDK)5.0附带的Xerces版本检查无效文档时的一些典型输出:

file:///Users/elharo/CS905/Course_Notes.xml is not valid because cvc-complex-type.2.3: Element 'legalnotice' cannot have character [children], because the type's content type is element-only.

您可以轻松更改要验证的架构,要验证的文档,甚至架构语言。 但是,在所有情况下,验证都遵循以下五个步骤:

  1. 加载用于模式编写语言的模式工厂。
  2. 从源代码编译模式。
  3. 从编译的架构中创建一个验证器。
  4. 为要验证的文档创建一个Source对象。 StreamSource通常是最简单的。
  5. 验证输入源。 如果文档无效,则validate()方法将引发SAXException 。 否则,它将安静地返回。

您可以连续多次重复使用相同的验证器和相同的架构。 但是,只有架构是线程安全的。 验证器和模式工厂不是。 如果同时在多个线程中进行验证,请确保每个线程都有自己的ValidatorSchemaFactory对象。

根据文档指定的架构进行验证

一些文档通常使用xsi:noNamespaceSchemaLocation和/或xsi:schemaLocation属性来指定希望对其进行验证的架构,如下所示:

<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="http://www.example.com/document.xsd">
  ...

如果在不指定URL,文件或源的情况下创建模式,则Java语言会创建一种语言,该语言会在正在验证的文档中查找以查找应使用的模式。 例如:

SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
Schema schema = factory.newSchema();

但是,通常这不是您想要的。 通常,文档使用者应选择架构,而不是文档生产者。 此外,这种方法仅适用于XSD。 所有其他架构语言都需要一个明确指定的架构位置。

抽象工厂

SchemaFactory是一个抽象工厂。 抽象工厂设计模式使这一API能够支持许多不同的模式语言和对象模型。 单个实现通常仅支持众多语言和模型的一部分。 但是,一旦学习了用于根据RELAX NG模式验证DOM文档的API(例如),就可以使用相同的API来针对W3C模式验证JDOM文档。

例如, 清单2显示了一个根据DocBook的RELAX NG模式验证DocBook文档的程序。 它与清单1几乎相同。 唯一更改的是架构的位置和标识架构语言的URL。

清单2.使用RELAX NG验证DocBook文档
import java.io.*;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.*;
import org.xml.sax.SAXException;

public class DocbookRELAXNGCheck {

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

        // 1. Specify you want a factory for RELAX NG
        SchemaFactory factory 
         = SchemaFactory.newInstance("http://relaxng.org/ns/structure/1.0");
        
        // 2. Load the specific schema you want. 
        // Here I load it from a java.io.File, but we could also use a 
        // java.net.URL or a javax.xml.transform.Source
        File schemaLocation = new File("/opt/xml/docbook/rng/docbook.rng");
        
        // 3. Compile the schema.
        Schema schema = factory.newSchema(schemaLocation);
    
        // 4. Get a validator from the schema.
        Validator validator = schema.newValidator();
        
        // 5. Parse the document you want to check.
        String input 
         = "file:///Users/elharo/Projects/workspace/CS905/build/Java_Course_Notes.xml";
        
        // 6. Check the document
        try {
            validator.validate(source);
            System.out.println(input + " is valid.");
        }
        catch (SAXException ex) {
            System.out.println(input + " is not valid because ");
            System.out.println(ex.getMessage());
        }  
        
    }

}

如果使用现有的Sun JDK运行此程序,而没有额外的库,则可能会看到以下内容:

Exception in thread "main" java.lang.IllegalArgumentException: 
http://relaxng.org/ns/structure/1.0
	at javax.xml.validation.SchemaFactory.newInstance(SchemaFactory.java:186)
	at DocbookRELAXNGCheck.main(DocbookRELAXNGCheck.java:14)

这是因为,开箱即用,JDK不包含RELAX NG验证器。 如果无法识别模式语言,则SchemaFactory.newInstance()会抛出IllegalArgumentException 。 但是,如果您安装诸如jing和JAXP 1.3适配器之类的RELAX NG库,则它应产生与W3C模式相同的答案。

识别模式语言

javax.xml.constants类定义几个常量以标识模式语言:

  • XMLConstants.W3C_XML_SCHEMA_NS_URIhttp://www.w3.org/2001/XMLSchema : http://www.w3.org/2001/XMLSchema
  • XMLConstants.RELAXNG_NS_URIhttp://relaxng.org/ns/structure/1.0 XMLConstants.RELAXNG_NS_URI
  • XMLConstants.XML_DTD_NS_URIhttp://www.w3.org/TR/REC-xml : http://www.w3.org/TR/REC-xml

这不是封闭列表。 实现是免费的,可以将其他URL添加到此列表中以标识其他架构语言。 通常,URL是模式语言的名称空间统一资源标识符(URI)。 例如,URL http://www.ascc.net/xml/schematron标识Schematron模式。

Sun的JDK 5仅支持XSD模式。 尽管支持DTD验证,但是无法通过javax.xml.validation API进行访问。 对于DTD,必须使用常规的SAX XMLReader类。 但是,您可以安装其他库,以添加对这些和其他架构语言的支持。

模式工厂如何定位

Java编程语言不限于单个架构工厂。 当您将标识特定模式语言的URI传递给SchemaFactory.newInstance() ,它将按此顺序搜索以下位置以找到匹配的工厂:

  1. "javax.xml.validation.SchemaFactory: schemaURL "系统属性命名的类
  2. $java.home/lib/jaxp.properties文件中找到的由"javax.xml.validation.SchemaFactory: schemaURL "属性命名的类
  3. 在任何可用的Java归档(JAR)文件的META-INF / services目录中找到javax.xml.validation.SchemaFactory服务提供者
  4. JDK 5中的平台默认SchemaFactorycom.sun.org.apache.xerces.internal.jaxp.validation.xs.SchemaFactoryImpl

为了增加对自己的自定义模式语言和相应的验证器的支持,您要做的就是编写SchemaFactorySchemaValidator子类,它们知道如何处理模式语言。 然后,在这四个位置之一中安装JAR。 这对于添加约束是很有用的,这些约束使用Java之类的图灵完备语言比声明性语言(如W3C XML Schema语言)更容易检查。 您可以定义一种迷你模式语言,编写一个快速实现,然后将其插入验证层。

错误处理程序

模式的默认响应是,如果有问题,则抛出SAXException如果没有问题,则不执行任何操作。 但是,可以提供SAX ErrorHandler来接收有关文档问题的更多详细信息。 例如,假设您要记录所有验证错误,但是当您遇到一个验证错误时,您不想停止处理。 您可以安装清单3所示的错误处理程序。

清单3.仅记录非致命有效性错误的错误处理程序
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

public class ForgivingErrorHandler implements ErrorHandler {

    public void warning(SAXParseException ex) {
        System.err.println(ex.getMessage());
    }

    public void error(SAXParseException ex) {
        System.err.println(ex.getMessage());
    }

    public void fatalError(SAXParseException ex) throws SAXException {
        throw ex;
    }

}

要安装此错误处理程序,请创建它的一个实例,然后将该实例传递给ValidatorsetErrorHandler()方法:

ErrorHandler lenient = new ForgivingErrorHandler();
  validator.setErrorHandler(lenient);

模式扩充

有些模式所做的不只是验证。 除了提供有关文档是否有效的问题的真假答案外,他们还使用其他信息来扩充文档。 例如,它们可以提供默认属性值。 他们还可以将int或gYear之类的类型分配给元素或属性。 验证器可以创建这种类型增强的文档,并将其写入到javax.xml.transform.Result对象中。 您需要做的就是传递一个Result作为第二个参数来验证。 例如, 清单4既验证了输入文档,又根据输入与模式的组合创建了增强的DOM文档。

清单4.使用模式扩充文档
import java.io.*;
import javax.xml.transform.dom.*;
import javax.xml.validation.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;

public class DocbookXSDAugmenter {

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

        SchemaFactory factory 
         = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
        File schemaLocation = new File("/opt/xml/docbook/xsd/docbook.xsd");
        Schema schema = factory.newSchema(schemaLocation);
        Validator validator = schema.newValidator();
        
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware(true); // never forget this
        DocumentBuilder builder = domFactory.newDocumentBuilder();
        Document doc = builder.parse(new File(args[0]));
        
        DOMSource source = new DOMSource(doc);
        DOMResult result = new DOMResult();
        
        try {
            validator.validate(source, result);
            Document augmented = (Document) result.getNode();
            // do whatever you need to do with the augmented document...
        }
        catch (SAXException ex) {
            System.out.println(args[0] + " is not valid because ");
            System.out.println(ex.getMessage());
        }  
        
    }

}

此过程无法将任意源转换为任意结果。 它对于流源和结果根本不起作用。 可以将SAX源扩展为SAX结果,将DOM源扩展为DOM结果。 但是SAX来源不能增加到DOM结果,反之亦然。 如果需要这样做,请首先将匹配结果扩充为SAX(对于SAX来说是SAX,对于DOM是DOM),然后使用TrAX的标识转换来更改模型。

不过,不建议使用此技术。 将文档所需的所有信息放在实例中比在实例和模式之间分割文档要可靠得多。 您可能会验证,但并非所有人都会。

类型信息

W3C XML Schema Language很大程度上基于类型的概念。 元素和属性声明为int,double,date,duration,person,PhoneNumber或其他任何您可以想象的类型。 Java验证API包括一种报告此类类型的方法,尽管它出人意料地独立于该软件包的其余部分。

类型由org.w3c.dom.TypeInfo对象标识。 清单5中概述了这个简单的界面,它告诉您类型的本地名称和名称空间URI。 您还可以判断一个类型是否以及如何从另一个类型派生。 除此之外,了解类型取决于您的程序。 Java语言不会告诉您这是什么意思,也不会将数据转换为Java类型(例如doublejava.util.Date

清单5. DOM TypeInfo接口
package org.w3c.dom;

public interface TypeInfo {

  public static final int DERIVATION_RESTRICTION;
  public static final int DERIVATION_EXTENSION;
  public static final int DERIVATION_UNION;

  public String  getTypeName();
  public String  getTypeNamespace()
  public boolean isDerivedFrom(String namespace, String name, int derivationMethod);

}

要获取TypeInfo对象,请向Schema对象请求ValidatorHandler而不是ValidatorValidatorHandler实现SAX的ContentHandler接口。 然后,您将此处理程序安装在SAX解析器中。

您还可以在ValidatorHandler (而不是解析器中)安装自己的ContentHandlerValidatorHandler会将增强事件转发到您的ContentHandler

ValidatorHandler使TypeInfoProvider可用, ContentHandler可以随时调用该TypeInfoProvider来查找当前元素的类型或其属性之一。 它还可以告诉您属性是否为ID,以及该属性是在文档中显式指定还是在架构中默认设置。 清单6总结了该类。

清单6. TypeInfoProvider类
package javax.xml.validation;

public abstract class TypeInfoProvider {

  public abstract TypeInfo getElementTypeInfo();
  public abstract TypeInfo getAttributeTypeInfo(int index);
  public abstract boolean  isIdAttribute(int index);
  public abstract boolean  isSpecified(int index);

}

最后,使用SAX XMLReader解析文档。 清单7显示了一个简单的程序,该程序使用所有这些类和接口来打印出文档中所有元素类型的名称。

清单7.列出元素类型
import java.io.*;
import javax.xml.validation.*;

import org.xml.sax.*;
import org.xml.sax.helpers.*;

public class TypeLister extends DefaultHandler {

    private TypeInfoProvider provider;
    
    public TypeLister(TypeInfoProvider provider) {
        this.provider = provider;
    }

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

        SchemaFactory factory 
         = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
        File schemaLocation = new File("/opt/xml/docbook/xsd/docbook.xsd");
        Schema schema = factory.newSchema(schemaLocation);
    
        ValidatorHandler vHandler = schema.newValidatorHandler();
        TypeInfoProvider provider = vHandler.getTypeInfoProvider();
        ContentHandler   cHandler = new TypeLister(provider);
        vHandler.setContentHandler(cHandler);
        
        XMLReader parser = XMLReaderFactory.createXMLReader();
        parser.setContentHandler(vHandler);
        parser.parse(args[0]);
        
    }
    
    public void startElement(String namespace, String localName,
      String qualifiedName, Attributes atts) throws SAXException {
        String type = provider.getElementTypeInfo().getTypeName();
        System.out.println(qualifiedName + ": " + type);
    }

}

这是在典型的DocBook文档上运行此代码的输出的开始:

book: #AnonType_book
title: #AnonType_title
subtitle: #AnonType_subtitle
info: #AnonType_info
copyright: #AnonType_copyright
year: #AnonType_year
holder: #AnonType_holder
author: #AnonType_author
personname: #AnonType_personname
firstname: #AnonType_firstname
othername: #AnonType_othername
surname: #AnonType_surname
personblurb: #AnonType_personblurb
para: #AnonType_para
link: #AnonType_link

如您所见,DocBook模式为大多数元素分配匿名复杂类型。 显然,这从一个模式到下一个模式将有所不同。

结论

如果每个人只讲一种语言,世界将是一个贫穷的地方。 如果程序员只有一种编程语言可供选择,他们将不满意。 不同的语言更好地适合不同的任务,有些任务需要不止一种语言。 XML模式没有什么不同。 您可以从大量有用的模式语言中进行选择。 在具有javax.xml.validation Java 5中,您具有可以处理所有这些内容的API。


翻译自: https://www.ibm.com/developerworks/java/library/x-javaxmlvalidapi/index.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值