如何使用资源 jar 中分解出的 .xsd 文件来指定 JAXB 模式?

How can I use factored-out .xsd files from a resource jar to specify a JAXB schema?

我正在将一个 XML 文件解组为 JAXB 生成的 Java 对象。我希望解组器在此过程中根据模式验证文件。架构的 .xsd 文件位于依赖项 .jar 文件中。我这样设置架构:

ClassLoader classLoader = getClass().getClassLoader();
InputStream schemaStream = classLoader.getResourceAsStream(schemaFilePath);
StreamSource schemaSource = new StreamSource(schemaStream);
Schema schema = factory.newSchema(schemaSource);
unmarshaller.setSchema(schema);

除一个问题外,大多数情况下都有效。该模式将一些复杂类型分解到其他 .xsd 文件中。解组器似乎无法找到分解出的 .xsd 文件,因为当我尝试设置架构时,我得到 SAXException:

Cannot resolve the name 'tns:FactoredOutType' to a(n) 'type definition' component.

注意:当从 eclipse 运行 引用 target 文件夹中的 .xsd 文件而不是 .jar 文件时,这工作正常。

任何人都知道如何让分解出的 .xsd 文件为依赖项的 .jar 文件中的模式工作?

编辑:

如果它是有用的信息,顶层 .xsdmodel 文件夹中,它引用的类型在 model/common 中,所以我引用顶层.xsd 作为:

"model/TopLevel.xsd"

... 在其中,它引用分解出的 .xsd 为:

"common/FactoredOut.xsd"

我们有一些 import/include 其他 XSD。我们通过将它们全部传递给 SchemaFactory 来实现这一点,而不是依赖导入来工作并在类路径中找到它。代码如下所示:

    try (InputStream xsdStream0 = ClaimLoadService.class.getResourceAsStream("/a.xsd");
            InputStream xsdStream1 = ClaimLoadService.class.getResourceAsStream("/b.xsd");
            InputStream xsdStream2 = ClaimLoadService.class.getResourceAsStream("/c.xsd");
            InputStream xsdStream3 = ClaimLoadService.class.getResourceAsStream("/d.xsd");
            InputStream xsdStream4 = ClaimLoadService.class.getResourceAsStream("/e.xsd");) {
        SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        schema = sf.newSchema(new Source[] { new StreamSource(xsdStream0), new StreamSource(xsdStream1),
                new StreamSource(xsdStream2), new StreamSource(xsdStream3), new StreamSource(xsdStream4) });
        ...
    } catch (SAXException | IOException | JAXBException e) {
        logger.log(Level.INFO, e.getMessage(), e);
    }

为了让 JAXB 完全满意,我们有时必须做的另一件事是确保导入模式实际引用导入的类型。我不记得表明我们需要采取解决方法的确切错误,但也许这就是您遇到的问题。这很俗气,但我们这样做是一种解决方法:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns="http://blah" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:name="http://blahblah" targetNamespace="http://blah"
elementFormDefault="qualified">

    <xs:import namespace="http://blahblah" />

    ...

    <xs:element name="ReferencesToMakeTheseElementsVisibleToThisJaxbContext">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="Business" type="name:type" minOccurs="0" />
                ...
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

基本上,我们创建一个 complexType 只是为了显式引用某些导入类型。只有当类型没有被直接引用时,我们才需要这样做。

通常,XML 相关的 API 有一种方法允许程序员指定辅助文件的位置,称为 资源解析。在模式的情况下,我认为你必须在调用 newSchema.

之前实现 LSResourceResolver 并将其传递给 SchemaFactory.setResourceResolver

我最终通过将对 newSchema 的调用从传递 StreamSource 切换为传递 URL 来解决问题,这保留了基本 URI,因此包含的类型仍然可以发现(正如@PetruGardea 在他的评论中提到的)。生成的代码如下所示:

URL schemaURL = objectType.getClassLoader().getResource(schemaPath);
if (schemaURL != null) {
    Schema schema = factory.newSchema(schemaURL);
    unmarshaller.setSchema(schema);
}

谢谢大家的帮助!