当前位置: 首页 > 知识库问答 >
问题:

从XJC中导入的架构解析类型定义失败

昌栋
2023-03-14

这个API使用JAXB来方便地使用对象模型,对象模型是由XJC(XML-to-Java)编译器通过命名引用从XML模式生成的。它抽象了JAXB上下文的创建,并通过各种背景魔法和反射找到ObjectFactory方法。它的基本要旨是,您总是定义一个通用模式,然后任何数量(也可能是0)的模式“扩展”该通用模式,每个模式产生自己的数据模型。通用模式携带可重用的定义,扩展它的定义使用这些定义组成自己的模型。

我现在遇到了这样的情况:我想为多个项目重用通用模式。一般类型定义应该在各个项目中保持不变,一些代码将根据这些类型定义生成的抽象类构建。所以我需要首先为一些通用模式生成类,然后分别生成扩展和使用它们的类。我将Maven用于构建过程。

我遇到的问题是在扩展模式中从泛型模式解析类型定义

假设我的泛型模式名为“general.xsd”,看起来如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.foobar.com/general"
xmlns:gen="http://www.foobar.com/general"
elementFormDefault="qualified" attributeFormDefault="qualified">

    <!-- Element (will usually be root) -->
    <xs:element name="transmission" type="gen:Transmission" />

    <!-- Definition -->
    <xs:complexType name="Transmission" abstract="true">
        <xs:sequence>
            <!-- Generic parts of a transmission would be in here... -->
        </xs:sequence>
    </xs:complexType>

</xs:schema>

接下来是一个绑定文件,用于进行一些命名定制,并为输出设置包名:

<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
    version="2.1">

    <!-- Bindings for the general schema -->
    <bindings schemaLocation="general.xsd" node="/xs:schema">

        <schemaBindings>
            <package name="com.foobar.models.general"/>
        </schemaBindings>

        <bindings node="//xs:complexType[@name='Transmission']">
            <!-- Some customization of property names here... -->
        </bindings>

</bindings>

然后在该项目的POM中有下一个位来生成Java类:

<plugin>
    <groupId>org.jvnet.jaxb2.maven2</groupId>
    <artifactId>maven-jaxb21-plugin</artifactId>
    <version>0.8.0</version>
    <executions>
        <execution>
            <id>xjc-generate</id>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <schemaDirectory>${basedir}/src/main/resources/com/foobar/schemas</schemaDirectory>
                <schemaLanguage>XMLSCHEMA</schemaLanguage>
                <addCompileSourceRoot>true</addCompileSourceRoot>
                <episode>true</episode>
                <removeOldOutput>true</removeOldOutput>
            </configuration>
        </execution>
    </executions>
</plugin>

正如您所看到的,我正在使用JAXB2.1 Maven插件。我已经将选项设置为生成一个插曲文件以进行逐步编译。删除以前输出的选项是为了解决bug问题;它所做的就是确保所有的东西都首先被清理干净,这样就会强制重新编译。

到目前为止还不错。那个项目编译起来毫无障碍。需要注意的是,除了生成的Java类之外,我还将模式打包到生成的jar文件中。所以这些在类路径上是可用的!sun-jaxb.epitter文件应该在META-INF中。

然后我开始使用模式的项目,该项目将通过首先导入它来扩展上面的内容。其中一个“子类型”可能如下所示(我将其称为sub.xsd):

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.foobar.com/sub"
xmlns:sub="http://www.foobar.com/sub"
xmlns:gen="http://www.foobar.com/general"
elementFormDefault="qualified" attributeFormDefault="qualified">

    <xs:import namespace="http://www.foobar.com/general" />

    <!-- Definition -->
    <xs:complexType name="SubTransmission">
        <xs:complexContent>
            <xs:extension base="gen:Transmission">
                <xs:sequence>
                    <!-- Additional elements placed here... -->
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

</xs:schema>

这里还有一个绑定文件:

<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
    version="2.1">

    <!-- Bindings for sub type -->
    <bindings schemaLocation="sub.xsd" node="/xs:schema">

        <schemaBindings>
            <package name="com.foobar.models.sub"/>
        </schemaBindings>

    </bindings>

</bindings>

下面是本项目POM中负责XJC生成的部分:

<plugin>
    <groupId>org.jvnet.jaxb2.maven2</groupId>
    <artifactId>maven-jaxb21-plugin</artifactId>
    <version>0.8.0</version>
    <executions>
        <execution>
            <id>xjc-generate</id>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <schemaDirectory>${basedir}/src/main/resources/com/foobar/schemas</schemaDirectory>
                <schemaLanguage>XMLSCHEMA</schemaLanguage>
                <addCompileSourceRoot>true</addCompileSourceRoot>
                <episode>false</episode>
                <catalog>${basedir}/src/main/resources/com/foobar/schemas/catalog.cat</catalog>
                <episodes>
                    <episode>
                        <groupId>com.foobar</groupId>
                        <artifactId>foobar-general-models</artifactId>
                        <version>1.0.0-SNAPSHOT</version>
                        <scope>compile</scope>
                    </episode>
                </episodes>
                <removeOldOutput>true</removeOldOutput>
            </configuration>
        </execution>
    </executions>
</plugin>

最初,所有模式都在一个文件夹中,并且我将导入中的schemaLocation属性设置为general.xsd,这可以很好地工作。但是现在事情在各个项目之间被分开了,我遇到了一些问题。第一个问题是找不到另一个模式。通过从 元素中取出schemaLocation属性,只保留namespace属性,并添加一个目录文件(catalog.cat),您可以在上面的POM摘录中看到它。其内容为:

PUBLIC "http://www.foobar.com/general" "classpath:/com/foobar/schemas/general.xsd"

这似乎起作用了,因为我不再得到一个声明无法找到模式的错误。但是由于某种原因,从导入的架构解析实际的类型定义仍然失败。这里有个例外:

Error while parsing schema(s).Location [ file:/C:/NetBeans_groups/Test/SubModelBundle/src/main/resources/com/foobar/schemas/sub.xsd{...,...}].
org.xml.sax.SAXParseException: src-resolve: Cannot resolve the name 'gen:Transmission' to a(n) 'type definition' component.

以下是我目前尝试的方法:

  • 使用目录文件。部分成功,因为现在可以找到导入的架构。
  • 让常规模式的编译生成一个插曲文件,并将其用于子模式的编译。这似乎没有什么区别,尽管这只应该在类型解析后才起作用,所以我认为这还不重要。
  • 使用不同的JAXP(注意:不是JAXB、JAXP)实现。它确实使用了不同的一个,因为我可以在异常的堆栈跟踪中看到这一点,但最终结果是相同的。
  • 使用maven-jaxb22-plugin而不是21。没有区别。

环顾网上,人们似乎至少从2006年就遇到了这个问题,它可能与一些Xerces解析器问题有关。我希望这不是一个已经潜伏了6年没有人关心修复它的bug。别人有什么建议吗?也许有人遇到了同样的问题并找到了解决方法?我能想到的唯一的解决办法是使用'svn:externals'将通用模式拖到子项目中,并在那里重新生成类,但这是肮脏的,只有当您可以连接到我们的svn repo时才会起作用。

非常感谢阅读这篇长篇文章。请记住,我已经从现有的项目中获取了上述所有内容,并替换了一些名称空间和其他匿名功能,因此可能会出现一些错别字。

共有1个答案

公冶渝
2023-03-14

这个答案是经过编辑的。之前,我有一个使用自定义目录解析器的解决方案。然而,我现在发现了实际的问题。解释如下。对于提供解决方案的tl;dr版本,请滚动到此答案的底部。

问题出在目录文件上。请注意它是如何有这一行的:

PUBLIC "http://www.foobar.com/general" "classpath:/com/foobar/schemas/general.xsd"

上面写了什么?它指出,如果遇到公共IDhttp://www.foobar.com/general,则模式的系统ID为classpath:/com/foobar/schemas/general.xsd。到目前为止还不错。如果我们从 元素中取出schemaLocation属性,那么剩下的唯一东西就是公共ID(命名空间URN),目录文件告诉我们在哪里可以找到它的模式。

当该架构使用 元素时,就会出现问题。它们包括具有相同目标命名空间的架构文件。它们指定一个系统ID(相对位置)。所以你希望它能被用来解决问题。但是,记录对编录解析器的调用会发现,请求是用公共ID(命名空间)和系统ID(相对位置)进行解析的。这就是它出问题的地方。由于编录文件中的绑定,因此优先使用public ID。这又将我们引到general.xsd文件。

例如,一般模式如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.foobar.com/general"
xmlns:gen="http://www.foobar.com/general"
elementFormDefault="qualified" attributeFormDefault="qualified">

    <!-- Including some definitions from another schema in the same location -->
    <xs:include schemaLocation="simple-types.xsd" />

    <!-- Remaining stuff... -->

</xs:schema>

并且使用该模式的架构如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.foobar.com/sub"
xmlns:sub="http://www.foobar.com/sub"
xmlns:gen="http://www.foobar.com/general"
elementFormDefault="qualified" attributeFormDefault="qualified">

    <xs:import namespace="http://www.foobar.com/general" />

    <!-- Remaining stuff... -->

</xs:schema>

当XJC解析最后一个模式时,会出现以下情况:

  1. 正在分析本地定义。
  2. 遇到对导入架构中定义的引用。
  3. 检查导入,未找到系统ID,仅找到公共ID(http://www.foobar.com/general)。
  4. 检查目录。
  5. 查找公共ID与类路径的绑定:/com/foobar/schemas/general.xsd
  6. 分析导入架构中的定义。
  7. 遇到对包含架构(simple-types.xsd)中定义的引用。
  8. 检查包括查找系统ID。
  9. 检查目录中的系统ID,但公共ID是隐式的。
  10. 查找公共ID与类路径:/com/foobar/schemas/general.xsd的绑定,该类路径优先于系统ID。
  11. 解析包含的架构定义失败。

因此,解决方案是指定系统ID是首选的解析方法,而不是在导入中提供系统ID,以便使用目录的公共ID绑定,并依赖于include中的相关系统ID。在OASIS XML目录格式中,可以使用属性prefer=“system”。在OASIS TR9401目录格式中,可以使用override no。显然默认值是public/yes。

因此我的目录文件变成:

OVERRIDE no
PUBLIC "http://www.foobar.com/general" "classpath:/com/foobar/schemas/general.xsd"

现在常规的目录解析器工作得很好。我不再需要定制的了。但是,我不会猜到在包含模式时仍然使用公共ID进行解析,并且优先于系统ID。我原以为public ID只用于导入,如果解析失败,系统ID仍将被考虑。只有向自定义冲突解决程序添加一些日志记录才揭示了这一点。

简单的回答是:将override no添加为TR9401目录文件中的第一个指令,或者将prefer=“system”属性添加到XML目录文件中。不要在 指令中指定schemaLocation,而是将命名空间绑定到目录文件中的适当架构位置。确保 使用包含架构的相对路径。

另一个有趣的事情是:XJC使用的目录解析器不仅可以处理classpath:URI,还可以处理Maven:URI,它们相对于Maven人工制品工作。如果您使用它作为构建工具,那么它是非常有用的。http://confluence.highsource.org/display/mjiip/user+guide#userguide-usingcatalogs

 类似资料:
  • 我一直在尝试解析xsd文件,以便能够在本地生成jaxb文件。为了做到这一点,我在linux上使用了命令,而不使用命令本身的任何选项。 但即使是我也非常基本地使用它: 它给出了一个错误,如下所示: 错误发生定义见下文第11行。 即使我一直在使用的xsd模式也是一个非常通用和正式的模式,它会给出上面看到的错误。因为它是默认模式,不应该被改变,我不知道我需要做什么... 在一些轮胎之后,我还尝试使用中的

  • 我需要帮助来找出如何解决活动中两个相互冲突的导入的问题,即: null 取决于哪一个先来。

  • 我试图在我的主文件。 entry.ts位于src/js/entry, ts,vue的类型定义位于src/js/lib/baul/vue/type/index.d.ts 编译此文件时,输出如下。顺便说一句,typescript的目标是es6和es6模块。 JQuery工作正常(看起来像),所以我用导入替换了对vue类型定义的引用。 它编译得很好,下面是输出: 问题是没有删除类型定义的import语句

  • > 导入React,{useState, use效应}从"react"; 从"yup"导入*as yup; const formSchema=yup.object().shape({ id:yup.string().required(“required”), 姓名:是的 类型:是的 值:yup.string().required(“required”), }); const PizzaForm=(

  • 可悲的是,我在搜索结果中找不到类似的东西。 换句话说,eclipse知道该项目,但导入不适合。

  • 我正在试图找出为什么我不能在<code>的“消息”: 当我试图解析模式时,我得到了以下错误: 线程“main” org.apache.avro.SchemaParseException中的异常:未定义的名称:“media_type”at org.apache.avro.Schema.parse(Schema.java:1162) at org.apache.avro.Schema.parse(Sc