1. 概述
在这个教程中,我们将演示如何使用Java验证一个XML文件与XSD文件的匹配性。
2. XML和XSD文件的定义
考虑以下包含姓名和地址的XML文件baeldung.xml
,地址由邮政编码和城市组成:
<?xml version="1.0" encoding="UTF-8" ?>
<individual>
<name>Baeldung</name>
<address>
<zip>00001</zip>
<city>New York</city>
</address>
</individual>
baeldung.xml
的内容完全符合person.xsd
文件的描述:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="individual">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="address">
<xs:complexType>
<xs:sequence>
<xs:element name="zip" type="xs:positiveInteger" />
<xs:element name="city" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
然而,我们的XML文件不符合full-person.xsd
文件的要求:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="individual">
<xs:complexType>
<xs:sequence>
<xs:element name="name">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="5" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="address">
<xs:complexType>
<xs:sequence>
<xs:element name="zip" type="xs:positiveInteger" />
<xs:element name="city" type="xs:string" />
<xs:element name="street" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
有两个问题:
- 姓名属性的最大长度限制为5个字符
- 地址期望有一个街道属性
接下来,我们将展示如何使用Java获取这些信息。
3. 验证XML文件与XSD文件
javax.xml.validation
包定义了XML文档验证的API。
首先,我们将准备一个能够读取遵循XML Schema 1.0规范的文件的SchemaFactory
。然后,我们将使用这个SchemaFactory
创建对应于我们的XSD文件的Schema
。Schema
表示一组约束。
最后,我们从Schema
中获取Validator
。Validator
是一个处理器,用于检查XML文档与Schema
的匹配:
private Validator initValidator(String xsdPath) throws SAXException {
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Source schemaFile = new StreamSource(getFile(xsdPath));
Schema schema = factory.newSchema(schemaFile);
return schema.newValidator();
}
在这个代码中,getFile
方法允许我们将XSD读入File
。在示例中,我们会将文件放在资源目录下,所以这个方法读取的是:
private File getFile(String location) {
return new File(getClass().getClassLoader().getResource(location).getFile());
}
值得注意的是,当我们创建Schema
时,如果XSD文件无效,可能会抛出SAXException
。
现在我们可以使用Validator
验证XML文件是否符合XSD描述。 validate
方法要求我们将File
转换为StreamSource
:
public boolean isValid() throws IOException, SAXException {
Validator validator = initValidator(xsdPath);
try {
validator.validate(new StreamSource(getFile(xmlPath)));
return true;
} catch (SAXException e) {
return false;
}
}
如果解析过程中出现错误,validate
方法会抛出SAXException
,这表明XML文件不符合XSD规范。
validate
方法也可能抛出IOException
,如果在读取File
时发生底层问题。
现在,我们可以将这些代码封装到XmlValidator
类中,并检查baeldung.xml
符合person.xsd
的描述,但不匹配full-person.xsd
:
@Test
public void givenValidXML_WhenIsValid_ThenTrue() throws IOException, SAXException {
assertTrue(new XmlValidator("person.xsd", "baeldung.xml").isValid());
}
@Test
public void givenInvalidXML_WhenIsValid_ThenFalse() throws IOException, SAXException {
assertFalse(new XmlValidator("full-person.xsd", "baeldung.xml").isValid());
}
4. 列出所有验证错误
validate
方法的基本行为是在解析抛出SAXException
时退出。
现在我们想要收集所有验证错误,需要改变这种行为。为此,我们需要自定义一个ErrorHandler
:
public class XmlErrorHandler implements ErrorHandler {
private List<SAXParseException> exceptions;
public XmlErrorHandler() {
this.exceptions = new ArrayList<>();
}
public List<SAXParseException> getExceptions() {
return exceptions;
}
@Override
public void warning(SAXParseException exception) {
exceptions.add(exception);
}
@Override
public void error(SAXParseException exception) {
exceptions.add(exception);
}
@Override
public void fatalError(SAXParseException exception) {
exceptions.add(exception);
}
}
然后,我们可以告诉Validator
使用这个特定的ErrorHandler
:
public List<SAXParseException> listParsingExceptions() throws IOException, SAXException {
XmlErrorHandler xsdErrorHandler = new XmlErrorHandler();
Validator validator = initValidator(xsdPath);
validator.setErrorHandler(xsdErrorHandler);
try {
validator.validate(new StreamSource(getFile(xmlPath)));
} catch (SAXParseException e)
{
// ...
}
xsdErrorHandler.getExceptions().forEach(e -> LOGGER.info(String.format("Line number: %s, Column number: %s. %s", e.getLineNumber(), e.getColumnNumber(), e.getMessage())));
return xsdErrorHandler.getExceptions();
}
由于baeldung.xml
满足person.xsd
的要求,所以在这种情况下不会列出任何错误。但是,当使用full-person.xsd
时,我们将打印以下错误消息:
XmlValidator - Line number: 3, Column number: 26. cvc-maxLength-valid: Value 'Baeldung' with length = '8' is not facet-valid with respect to maxLength '5' for type '#AnonType_nameindividual'.
XmlValidator - Line number: 3, Column number: 26. cvc-type.3.1.3: The value 'Baeldung' of element 'name' is not valid.
XmlValidator - Line number: 7, Column number: 15. cvc-complex-type.2.4.b: The content of element 'address' is not complete. One of '{street}' is expected.
我们在第1部分提到的所有错误都被程序找到了。
5. 总结
在这篇文章中,我们了解了如何验证XML文件与XSD文件,并且还能列出所有验证错误。
如往常一样,代码可以在GitHub上找到。