针对XSD文件验证XML文件的最佳方法是什么?


263

我正在生成一些需要符合给我的xsd文件的xml文件。验证其符合性的最佳方法是什么?

Answers:


336

Java运行时库支持验证。上次我检查的是幕后的Apache Xerces解析器。您可能应该使用javax.xml.validation.Validator

import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.*;
import java.net.URL;
import org.xml.sax.SAXException;
//import java.io.File; // if you use File
import java.io.IOException;
...
URL schemaFile = new URL("http://host:port/filename.xsd");
// webapp example xsd: 
// URL schemaFile = new URL("http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd");
// local file example:
// File schemaFile = new File("/location/to/localfile.xsd"); // etc.
Source xmlFile = new StreamSource(new File("web.xml"));
SchemaFactory schemaFactory = SchemaFactory
    .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
try {
  Schema schema = schemaFactory.newSchema(schemaFile);
  Validator validator = schema.newValidator();
  validator.validate(xmlFile);
  System.out.println(xmlFile.getSystemId() + " is valid");
} catch (SAXException e) {
  System.out.println(xmlFile.getSystemId() + " is NOT valid reason:" + e);
} catch (IOException e) {}

模式工厂常量是http://www.w3.org/2001/XMLSchema定义XSD 的字符串。上面的代码针对URL验证了WAR部署描述符,http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd但您同样可以针对本地文件进行验证。

您不应使用DOMParser来验证文档(除非您的目标仍然是创建文档对象模型)。这将在解析文档时开始创建DOM对象-如果您不打算使用它们,那将很浪费。


在此示例中,您使用的是DOM还是SAX解析器?我怎么知道您正在使用哪个解析器,因为我看不到任何引用。
ziggy

1
@ziggy-这是JAXP实现的实现细节。Sun的JDK 6将SAX解析器与StreamSource一起使用。在这种情况下,JAXP实现可以合法地使用DOM解析器,但是没有理由。如果显式使用DOM解析器进行验证,则肯定会实例化DOM树。
McDowell

如何将ErrorHandler与上述方法配合使用?是否只是创建ErrorHandler并将其与验证器相关联的一种情况?即validator.SetErrorHandler(),如该SO问题中的示例stackoverflow.com/questions/4864681/…
ziggy

执行不是应该用于执行情况而不是控制流吗?
mike

此代码不会仅捕获致命错误吗?如果您希望能够捕获非致命事件(例如非结构性事件),我认为您将需要使用ErrorHandler。
matt forsythe,2014年

25

这是使用Xerces2的方法。为此的教程,在这里(要求注册)。

原始出处:从此处公然复制:

import org.apache.xerces.parsers.DOMParser;
import java.io.File;
import org.w3c.dom.Document;

public class SchemaTest {
  public static void main (String args[]) {
      File docFile = new File("memory.xml");
      try {
        DOMParser parser = new DOMParser();
        parser.setFeature("http://xml.org/sax/features/validation", true);
        parser.setProperty(
             "http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation", 
             "memory.xsd");
        ErrorChecker errors = new ErrorChecker();
        parser.setErrorHandler(errors);
        parser.parse("memory.xml");
     } catch (Exception e) {
        System.out.print("Problem parsing the file.");
     }
  }
}

9
SAX解析器将更加高效-DOM解析器创建DOM对象;这种情况下的浪费操作。
McDowell

问题是针对XSD验证XML。在此答案中,您将走得更远并获得一个不需要的Parser对象,对吗?
Weslor

“ ErrorChecker也无法解析为类型” ..缺少导入?
亚历克斯


13

由于这是一个流行的问题,我要指出的是java的,也验证对“简称” XSD的,例如,如果.xml文件本身指定XSD在标题中,使用xsi:SchemaLocationxsi:noNamespaceSchemaLocation(或XSI特定命名空间)EX

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

或SchemaLocation(始终是名称空间到xsd映射的列表)

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

其他答案也可以在这里使用,因为.xsd文件“映射”到.xml文件中声明的名称空间,因为它们声明了名称空间,并且如果与.xml文件中的名称空间匹配,那就很好。但是有时候拥有自定义解析器会很方便...

在javadocs中:“如果创建模式时未指定URL,文件或源,那么Java语言会创建一种模式,该模式在正在验证的文档中查找以查找其应使用的模式。例如:”

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

这种方法的问题在于,xmlsns:xsi它可能是网络位置,因此默认情况下,它将通过每次验证都出局并进入网络,而并非总是最优的。

这是一个示例,该示例针对XML文件所引用的任何XSD(即使必须将其从网络中拉出)也进行了验证:

  public static void verifyValidatesInternalXsd(String filename) throws Exception {
    InputStream xmlStream = new new FileInputStream(filename);
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setValidating(true);
    factory.setNamespaceAware(true);
    factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
                 "http://www.w3.org/2001/XMLSchema");
    DocumentBuilder builder = factory.newDocumentBuilder();
    builder.setErrorHandler(new RaiseOnErrorHandler());
    builder.parse(new InputSource(xmlStream));
    xmlStream.close();
  }

  public static class RaiseOnErrorHandler implements ErrorHandler {
    public void warning(SAXParseException e) throws SAXException {
      throw new RuntimeException(e);
    }
    public void error(SAXParseException e) throws SAXException {
      throw new RuntimeException(e);
    }
    public void fatalError(SAXParseException e) throws SAXException {
      throw new RuntimeException(e);
    }
  }

通过手动指定xsd(请参阅此处的其他答案)或使用“ XML目录” 样式的解析器,即使xml文件引用的是url,也可以避免从网络中提取引用的XSD 。Spring显然也可以拦截 URL请求以提供本地文件进行验证。或者,您可以通过setResourceResolver设置自己的属性,例如:

Source xmlFile = new StreamSource(xmlFileLocation);
SchemaFactory schemaFactory = SchemaFactory
                                .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema();
Validator validator = schema.newValidator();
validator.setResourceResolver(new LSResourceResolver() {
  @Override
  public LSInput resolveResource(String type, String namespaceURI,
                                 String publicId, String systemId, String baseURI) {
    InputSource is = new InputSource(
                           getClass().getResourceAsStream(
                          "some_local_file_in_the_jar.xsd"));
                          // or lookup by URI, etc...
    return new Input(is); // for class Input see 
                          // https://stackoverflow.com/a/2342859/32453
  }
});
validator.validate(xmlFile);

另请参见此处以获取其他教程。

我相信,默认是使用DOM解析,你可以用SAX解析器是验证类似的东西,以及 saxReader.setEntityResolver(your_resolver_here);


对我不起作用,除非在schemaFactory上设置了方法resolveResource(),否则它不会被调用?
tomasb

邓诺,为我工作。确保通过设置,setResourceResolver但除此之外,还可以打开新问题……
rogerdpack

6

使用Java 7,您可以按照软件包说明中提供的文档进行操作。

// create a SchemaFactory capable of understanding WXS schemas
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

// load a WXS schema, represented by a Schema instance
Source schemaFile = new StreamSource(new File("mySchema.xsd"));
Schema schema = factory.newSchema(schemaFile);

// create a Validator instance, which can be used to validate an instance document
Validator validator = schema.newValidator();

// validate the DOM tree
try {
    validator.validate(new StreamSource(new File("instance.xml"));
} catch (SAXException e) {
    // instance document is invalid!
}

2
“使用Java7。” 实际上包含在Java 5中
Andrew Thompson

4
这基本上与接受的答案相同。不过,在我看来,该解决方案效率不高,因为它不必要地构建xml来解析的DOM :parser.parse(new File("instance.xml"))。该validator接受Source,所以您可以:validator.validate(new StreamSource(new File("instance.xml")))
艾伯托2014年

以这种方式工作,将在xml文件中的第一个错误时抛出SAXException,然后停止验证。但是我想知道所有(!)错误。如果我改用ErrorHandler(实现ErrorHandler的自己的类),则它可以识别所有错误,但是validator.validate的try-catch-block不会引发任何异常。 -我的验证器方法?谢谢你的帮助!
mrbela 2015年

有“错误”(例如验证错误)和“致命错误”(格式错误)。一个致命错误通常会停止解析。但是验证错误并不能阻止它:您必须显式抛出异常。因此,ErrorHandler如果需要进行验证,则必须提供一个。
Ludovic Kuty

1
要承认,代码看起来比接受的答案更简洁,更易于阅读。
发条

3

如果您拥有Linux机器,则可以使用免费的命令行工具SAXCount。我觉得这很有用。

SAXCount -f -s -n my.xml

它针对dtd和xsd进行验证。50MB文件5秒。

在debian squeeze中,它位于软件包“ libxerces-c-samples”中。

dtd和xsd的定义必须在xml中!您不能单独配置它们。


2
这允许从vim(:!SAXCount -f -n -s%)进行简单的XML验证
Shane

4
或使用古老的xmllint xmllint --schema phone.xsd phone.xml(来自13ren 的回答)
rogerdpack

3

另一个答案:由于您说需要验证正在生成(写入)的文件,因此您可能想在写入时验证内容,而不是先写入然后再读回以进行验证。如果您使用基于SAX的writer,则可以使用JDK API进行Xml验证来实现这一点:如果是这样,只需调用“ Validator.validate(source,result)”链接验证器,其中source来自您的writer,结果是需要输出的地方。

另外,如果您使用Stax编写内容(或使用或可以使用stax的库),则在使用XMLStreamWriter时,Woodstox也可以直接支持验证。这是显示此操作方式的博客条目


嘿StaxMan,有没有XMLStreamWriters可以进行漂亮的打印缩进?我很惊讶它不在标准实现中。另外,它有很多用途吗?我认为这是正确的方法,但是似乎对此几乎没有兴趣。
2009年

刚刚发现您的文章在这里约StaxMate(但它不是化XMLStreamWriter):stackoverflow.com/questions/290326/stax-xml-formatting-in-java/...
13ren

是的,StaxMate可以做到。它内部使用XMLStreamWriter编写内容,因此您也可以通过这种方式连接验证器。
StaxMan

2

如果要以编程方式生成XML文件,则可能需要查看XMLBeans库。使用命令行工具,XMLBeans将基于XSD自动生成并打包一组Java对象。然后,您可以使用这些对象基于此架构构建XML文档。

它具有对模式验证的内置支持,并且可以将Java对象转换为XML文档,反之亦然。

CastorJAXB是其他Java库,其目的类似于XMLBeans。


1

使用JAXB,您可以使用以下代码:

    @Test
public void testCheckXmlIsValidAgainstSchema() {
    logger.info("Validating an XML file against the latest schema...");

    MyValidationEventCollector vec = new MyValidationEventCollector();

    validateXmlAgainstSchema(vec, inputXmlFileName, inputXmlSchemaName, inputXmlRootClass);

    assertThat(vec.getValidationErrors().isEmpty(), is(expectedValidationResult));
}

private void validateXmlAgainstSchema(final MyValidationEventCollector vec, final String xmlFileName, final String xsdSchemaName, final Class<?> rootClass) {
    try (InputStream xmlFileIs = Thread.currentThread().getContextClassLoader().getResourceAsStream(xmlFileName);) {
        final JAXBContext jContext = JAXBContext.newInstance(rootClass);
        // Unmarshal the data from InputStream
        final Unmarshaller unmarshaller = jContext.createUnmarshaller();

        final SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        final InputStream schemaAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(xsdSchemaName);
        unmarshaller.setSchema(sf.newSchema(new StreamSource(schemaAsStream)));

        unmarshaller.setEventHandler(vec);

        unmarshaller.unmarshal(new StreamSource(xmlFileIs), rootClass).getValue(); // The Document class is the root object in the XML file you want to validate

        for (String validationError : vec.getValidationErrors()) {
            logger.trace(validationError);
        }
    } catch (final Exception e) {
        logger.error("The validation of the XML file " + xmlFileName + " failed: ", e);
    }
}

class MyValidationEventCollector implements ValidationEventHandler {
    private final List<String> validationErrors;

    public MyValidationEventCollector() {
        validationErrors = new ArrayList<>();
    }

    public List<String> getValidationErrors() {
        return Collections.unmodifiableList(validationErrors);
    }

    @Override
    public boolean handleEvent(final ValidationEvent event) {
        String pattern = "line {0}, column {1}, error message {2}";
        String errorMessage = MessageFormat.format(pattern, event.getLocator().getLineNumber(), event.getLocator().getColumnNumber(),
                event.getMessage());
        if (event.getSeverity() == ValidationEvent.FATAL_ERROR) {
            validationErrors.add(errorMessage);
        }
        return true; // you collect the validation errors in a List and handle them later
    }
}

0

您在寻找工具还是库?

就库而言,事实上的标准是Xerces2,它同时具有C ++Java版本。

不过请注意,这是一个沉重的解决方案。但是话又说回来,针对XSD文件验证XML是一个相当沉重的问题。

至于为您执行此操作的工具,XMLFox似乎是一个不错的免费软件解决方案,但我不能肯定地说自己没有亲自使用过它。


0

根据在线模式进行验证

Source xmlFile = new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream("your.xml"));
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(Thread.currentThread().getContextClassLoader().getResource("your.xsd"));
Validator validator = schema.newValidator();
validator.validate(xmlFile);

根据本地架构进行验证

使用Java进行离线XML验证


0

使用Woodstox,配置StAX解析器以针对您的架构进行验证并解析XML。

如果捕获到异常,则XML无效,否则有效:

// create the XSD schema from your schema file
XMLValidationSchemaFactory schemaFactory = XMLValidationSchemaFactory.newInstance(XMLValidationSchema.SCHEMA_ID_W3C_SCHEMA);
XMLValidationSchema validationSchema = schemaFactory.createSchema(schemaInputStream);

// create the XML reader for your XML file
WstxInputFactory inputFactory = new WstxInputFactory();
XMLStreamReader2 xmlReader = (XMLStreamReader2) inputFactory.createXMLStreamReader(xmlInputStream);

try {
    // configure the reader to validate against the schema
    xmlReader.validateAgainst(validationSchema);

    // parse the XML
    while (xmlReader.hasNext()) {
        xmlReader.next();
    }

    // no exceptions, the XML is valid

} catch (XMLStreamException e) {

    // exceptions, the XML is not valid

} finally {
    xmlReader.close();
}

注意:如果需要验证多个文件,则应尝试重用XMLInputFactoryXMLValidationSchema,以最大化性能。


-3

我只需要针对XSD验证XML,所以尝试了XMLFox。我发现它非常令人困惑和奇怪。帮助说明似乎与界面不匹配。

我最终使用了LiquidXML Studio 2008(v6),它更易于使用并且更加直接(UI与我经常使用的Visual Basic 2008 Express非常相似)。缺点:验证功能不是免费版本,因此我不得不使用30天试用版。


1
问题是Java,但答案不是。:-(
james.garriss 2015年

公平地讲,“ java”一词永远不会出现在问题中,而只会出现在标签中。我想为此而不是答复。
Mark Storer

谢谢詹姆斯和马克,帮我磨练!
Knom
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.