Java 根据多个架构定义验证XML文件

Java 根据多个架构定义验证XML文件,java,xsd,xerces,Java,Xsd,Xerces,我正在尝试根据许多不同的模式验证XML文件(为人为的示例道歉): a、 xsd b、 xsd c、 xsd c、 特别是xsd导入b.xsd和b.xsd导入a.xsd,使用: 我试图通过Xerces以以下方式实现这一点: XMLSchemaFactory xmlSchemaFactory = new XMLSchemaFactory(); Schema schema = xmlSchemaFactory.newSchema(new StreamSource[] { new StreamSo

我正在尝试根据许多不同的模式验证XML文件(为人为的示例道歉):

  • a、 xsd
  • b、 xsd
  • c、 xsd
c、 特别是xsd导入b.xsd和b.xsd导入a.xsd,使用:

我试图通过Xerces以以下方式实现这一点:

XMLSchemaFactory xmlSchemaFactory = new XMLSchemaFactory();
Schema schema = xmlSchemaFactory.newSchema(new StreamSource[] { new StreamSource(this.getClass().getResourceAsStream("a.xsd"), "a.xsd"),
                                                         new StreamSource(this.getClass().getResourceAsStream("b.xsd"), "b.xsd"),
                                                         new StreamSource(this.getClass().getResourceAsStream("c.xsd"), "c.xsd")});     
Validator validator = schema.newValidator();
validator.validate(new StreamSource(new StringReader(xmlContent)));
但这无法正确导入所有三个模式,导致无法将名称“blah”解析为(n)“group”组件


我已经使用Python成功地验证了这一点,但在Java 6.0和Xerces 2.8.1方面存在实际问题。有谁能建议这里出了什么问题,或者一种更简单的方法来验证我的XML文档吗?

Xerces中的模式内容(a)非常非常迂腐,并且(b)在不喜欢它所发现的内容时给出了完全无用的错误消息。这是一个令人沮丧的组合

python中的模式可能更宽容,让模式中的小错误不被报告


现在,如您所说,如果c.xsd包含b.xsd,而b.xsd包含a.xsd,那么就不需要将这三个都加载到模式工厂中。这不仅是不必要的,而且可能会混淆Xerces并导致错误,所以这可能是您的问题。只需将c.xsd传递给工厂,让它自己解析b.xsd和a.xsd,这是相对于c.xsd应该做的

因此,为了防止其他人在这里遇到同样的问题,我需要从单元测试加载父模式(以及隐式子模式),作为一种资源,以验证XML字符串。我使用Xerces XMLSchemFactory和Java6验证器来实现这一点

为了通过include正确加载子模式,我必须编写一个自定义资源解析器。代码可在此处找到:

要使用解析器,请在架构工厂中指定它:

xmlSchemaFactory.setResourceResolver(new ResourceResolver());
它将使用它通过类路径解析您的资源(在我的例子中是从src/main/resources)。欢迎对此……发表任何意见。

节“单个文档的多个架构”

我基于该文档的解决方案:

URL xsdUrlA=this.getClass().getResource(“a.xsd”);
URL xsdUrlB=this.getClass().getResource(“b.xsd”);
URL xsdUrlC=this.getClass().getResource(“c.xsd”);
SchemaFactory SchemaFactory=SchemaFactory.newInstance(xmlstants.W3C\u XML\u SCHEMA\u NS\u URI);
//---
字符串W3C_XSD_TOP_元素=
“\n”
+“\n”
+“\n”
+“\n”
+“\n”
+"";
Schema Schema=schemaFactory.newSchema(新的StreamSource(新的StringReader(W3C_XSD_TOP_元素),“xsdTop”);

来自xerces文档:

我最后用了这个:

import org.apache.xerces.parsers.SAXParser;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import java.io.IOException;
 .
 .
 .
 try {
        SAXParser parser = new SAXParser();
        parser.setFeature("http://xml.org/sax/features/validation", true);
        parser.setFeature("http://apache.org/xml/features/validation/schema", true);
        parser.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true);
        parser.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation", "http://your_url_schema_location");

        Validator handler = new Validator();
        parser.setErrorHandler(handler);
        parser.parse("file:///" + "/home/user/myfile.xml");

 } catch (SAXException e) {
    e.printStackTrace();
 } catch (IOException ex) {
    e.printStackTrace();
 }


class Validator extends DefaultHandler {
    public boolean validationError = false;
    public SAXParseException saxParseException = null;

    public void error(SAXParseException exception)
            throws SAXException {
        validationError = true;
        saxParseException = exception;
    }

    public void fatalError(SAXParseException exception)
            throws SAXException {
        validationError = true;
        saxParseException = exception;
    }

    public void warning(SAXParseException exception)
            throws SAXException {
    }
}
记住要改变:

1) 参数”http://your_url_schema_location“为您提供xsd文件位置

2) 指向xml文件的字符串为“/home/user/myfile.xml”


我不必设置变量:
-Djavax.xml.validation.SchemaFactory:http://www.w3.org/2001/XMLSchema=org.apache.xerces.jaxp.validation.XMLSchemaFactory

我也面临同样的问题,在调查之后,我找到了这个解决方案。它对我有用

Enum
要设置不同的
xsd

public enum XsdFile {
    // @formatter:off
    A("a.xsd"),
    B("b.xsd"),
    C("c.xsd");
    // @formatter:on

    private final String value;

    private XsdFile(String value) {
        this.value = value;
    }

    public String getValue() {
        return this.value;
    }
}
要验证的方法:

public static void validateXmlAgainstManyXsds() {
    final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

    String xmlFile;
    xmlFile = "example.xml";

    // Use of Enum class in order to get the different XSDs
    Source[] sources = new Source[XsdFile.class.getEnumConstants().length];
    for (XsdFile xsdFile : XsdFile.class.getEnumConstants()) {
        sources[xsdFile.ordinal()] = new StreamSource(xsdFile.getValue());
    }

    try {
        final Schema schema = schemaFactory.newSchema(sources);
        final Validator validator = schema.newValidator();
        System.out.println("Validating " + xmlFile + " against XSDs " + Arrays.toString(sources));
        validator.validate(new StreamSource(new File(xmlFile)));
    } catch (Exception exception) {
        System.out.println("ERROR: Unable to validate " + xmlFile + " against XSDs " + Arrays.toString(sources)
                + " - " + exception);
    }
    System.out.println("Validation process completed.");
}

以防万一,还有人来这里寻找针对多个XSD验证xml或对象的解决方案,我在这里提到它

//Using **URL** is the most important here. With URL, the relative paths are resolved for include, import inside the xsd file. Just get the parent level xsd here (not all included xsds).

URL xsdUrl = getClass().getClassLoader().getResource("my/parent/schema.xsd");

SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(xsdUrl);

JAXBContext jaxbContext = JAXBContext.newInstance(MyClass.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setSchema(schema);

/* If you need to validate object against xsd, uncomment this
ObjectFactory objectFactory = new ObjectFactory();
JAXBElement<MyClass> wrappedObject = objectFactory.createMyClassObject(myClassObject); 
marshaller.marshal(wrappedShipmentMessage, new DefaultHandler());
*/

unmarshaller.unmarshal(getClass().getClassLoader().getResource("your/xml/file.xml"));
//这里最重要的是使用**URL**。使用URL,在xsd文件中解析include、import的相对路径。只需在此处获取父级xsd(并非所有包含的xsd)。
URL xsdUrl=getClass().getClassLoader().getResource(“my/parent/schema.xsd”);
SchemaFactory SchemaFactory=SchemaFactory.newInstance(xmlstants.W3C\u XML\u SCHEMA\u NS\u URI);
Schema=schemaFactory.newSchema(xsdUrl);
JAXBContext JAXBContext=JAXBContext.newInstance(MyClass.class);
Unmarshaller Unmarshaller=jaxbContext.createUnmarshaller();
解组器。设置模式(模式);
/*如果需要根据xsd验证对象,请取消对该对象的注释
ObjectFactory ObjectFactory=新的ObjectFactory();
JAXBElement-wrappedObject=objectFactory.createMyClassObject(myClassObject);
marshaller.marshall(wrappedShipmentMessage,newDefaultHandler());
*/
unmarshaller.unmarshal(getClass().getClassLoader().getResource(“your/xml/file.xml”));

如果所有XSD都属于同一名称空间,则创建一个新XSD并将其他XSD导入其中。然后在java中使用新的XSD创建模式

Schema schema = xmlSchemaFactory.newSchema(
    new StreamSource(this.getClass().getResourceAsStream("/path/to/all_in_one.xsd"));
_one.xsd中的所有_:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:ex="http://example.org/schema/" 
 targetNamespace="http://example.org/schema/" 
 elementFormDefault="unqualified"
 attributeFormDefault="unqualified">

    <xs:include schemaLocation="relative/path/to/a.xsd"></xs:include>
    <xs:include schemaLocation="relative/path/to/b.xsd"></xs:include>
    <xs:include schemaLocation="relative/path/to/c.xsd"></xs:include>

</xs:schema>


是的,这似乎也会导致相同的错误。我想知道模式文件中的导入声明是否会导致问题。。。两个模式也没有目标名称空间,这也没有帮助。。。Gargh解决这个问题的方法之一可能是使用ResourceResolver并在模式工厂中设置它……您确定没有混淆导入和包含吗?它们意味着两种不同的东西,不应该混淆。a、b和c在不同的名称空间中吗?如果是,则应将其输入,而不是包括在内。如果它们在同一个名称空间中,就应该包含它们。我没有这样编写模式,也不能更改它们,而是使用了include-它们在不同的名称空间中-不太清楚为什么。我必须编写一个自定义解析器并导入根模式,以使其最终工作。。。但无论如何,感谢您在加载根模式时给出的指针…@skaffman我了解到XSD的顺序可能非常重要。例如,我有两个xsd文件a.xsd和b.xsd。在我的xml文件中,首先使用属于a.xsd的名称空间,然后使用属于b.xsd的下一个名称空间。所以我必须根据a.xsd、b.xsd(不是b.xsd、a.xsd)验证xml文件,但我手动检测到了这一点。我怎样才能自动检测到它呢?有没有可能进一步解释一下自定义资源解析器是如何使这一切正常工作的?谢谢。我可以补充说,您必须添加以下内容:

Schema schema = xmlSchemaFactory.newSchema(
    new StreamSource(this.getClass().getResourceAsStream("/path/to/all_in_one.xsd"));
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:ex="http://example.org/schema/" 
 targetNamespace="http://example.org/schema/" 
 elementFormDefault="unqualified"
 attributeFormDefault="unqualified">

    <xs:include schemaLocation="relative/path/to/a.xsd"></xs:include>
    <xs:include schemaLocation="relative/path/to/b.xsd"></xs:include>
    <xs:include schemaLocation="relative/path/to/c.xsd"></xs:include>

</xs:schema>