我使用JAXB来使用这个API来方便地使用XJC(XML-to-Java)编译器通过命名引用从XML Schema生成的对象模型。它抽象了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插件。我已经设置了为逐步编译生成一个剧集文件的选项。删除以前输出的选项是针对错误的解决方法;它所做的就是确保所有内容都先清理干净,以便重新编译。
到目前为止一切顺利。那个项目编译顺利。应该注意的是,除了生成的Java类之外,我还将模式打包到生成的jar文件中。所以这些都可以在classpath上找到! sun-jaxb.episode
文件在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
元素中取出<xs:import />
属性,仅保留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.
这是我到目前为止所尝试的内容:
maven-jaxb22-plugin
代替21.没有区别。在线查看,似乎人们至少从2006年开始遇到这个问题,这可能与某些Xerces解析器问题有关。我希望这不是一个潜伏了6年的错误,没有任何人愿意修复它。别人有什么建议吗?也许有人遇到了同样的问题并找到了解决方案?我能想到的唯一解决方法是使用'svn:externals'将一般模式拖到子项目中,然后在那里重新生成类,但它很脏,只有在你可以连接到我们的svn repo时才能工作。
非常感谢您阅读这篇长篇文章。请记住,我已从现有项目中获取了上述所有内容,并替换了一些名称空间和其他内容以匿名,因此可能存在一些拼写错误。
答案 0 :(得分:4)
这个答案已被编辑。以前,我有一个使用自定义目录解析器的解决方案。但是,我现在发现了实际问题。解释如下。对于提供解决方案的TL; DR版本,请滚动到此答案的底部。
问题在于目录文件。请注意它是如何产生这一行的:
PUBLIC "http://www.foobar.com/general" "classpath:/com/foobar/schemas/general.xsd"
这是什么意思?它表示如果遇到公共标识http://www.foobar.com/general
,则模式的系统标识为classpath:/com/foobar/schemas/general.xsd
。到现在为止还挺好。如果我们从schemaLocation
元素中取出<xs:import />
属性,唯一剩下的就是公共ID(名称空间URN),目录文件告诉我们在哪里找到它的模式。
当该架构使用<xs:include />
元素时,会出现问题。它们包括具有相同目标命名空间的模式文件。它们指定系统ID(相对位置)。所以你希望用它来解决问题。但是,将调用记录到目录解析程序会显示请求是使用公共ID(名称空间)和系统ID(相对位置)进行解析的。这就是它出错的地方。由于目录文件中的绑定,公共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解析最后一个模式时,会发生这种情况:
http://www.foobar.com/general
)。classpath:/com/foobar/schemas/general.xsd
的绑定。classpath:/com/foobar/schemas/general.xsd
,它优先于系统ID。有关解析的顺序的详细信息,请参阅OASIS规范中的XML目录:https://www.oasis-open.org/committees/entity/spec.html#s.ext.ent。它需要一些解释,但您会发现,如果首选的解析方法是公共ID,那么即使存在系统ID,它们也会在目录文件中绑定时优先。
然后,解决方案是指定系统ID是首选的解析方法,不在导入中提供系统ID,以便使用目录的公共ID绑定,并依赖于包含的相关系统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。我原以为公共ID只会用于导入,如果解析失败,系统ID仍会被考虑。仅向自定义解析程序添加一些日志记录显示了这一点。
简短回答:将OVERRIDE no
添加为TR9401目录文件中的第一个指令,或将属性prefer="system"
添加到XML目录文件中。不要在schemaLocation
指令中指定<xs:import />
,而是将命名空间绑定到目录文件中的正确模式位置。确保<xs:include />
使用包含架构的相对路径。
另一个有趣的事情是:XJC使用的目录解析器不仅可以处理classpath:
URI,还可以处理maven:
URI,它们相对于Maven工件而言。如果您将其用作构建工具,那将非常有用。
http://confluence.highsource.org/display/MJIIP/User+Guide#UserGuide-Usingcatalogs
答案 1 :(得分:2)
使用 maven 2.2.1 可以使用 org.jvnet.jaxb2.maven2.resolver.tools.ClasspathCatalogResolver 。
以下是一个示例配置:
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.8.0</version>
<executions>
<execution>
<id>executionId</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<schemaDirectory>src/main/resources/META-INF/schemas</schemaDirectory>
<generatePackage>com.company.project.data</generatePackage>
<bindingDirectory>src/main/jaxb</bindingDirectory>
<catalog>src/main/jaxb/catalog.cat</catalog>
<catalogResolver>org.jvnet.jaxb2.maven2.resolver.tools.ClasspathCatalogResolver</catalogResolver>
<verbose>false</verbose>
<extension>true</extension>
<episodes>
<episode>
<groupId>com.company.project</groupId>
<artifactId>xsd-common-types</artifactId>
<version>${xsd-common-types.version}</version>
</episode>
</episodes>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.company.project</groupId>
<artifactId>xsd-common-types</artifactId>
<version>${xsd-common-types.version}</version>
</dependency>
</dependencies>
</plugin>
使此配置与Maven 3一起使用会导致org.xml.sax.SAXParseException