how to create common xsd generated java classes

2019-03-06 14:36发布

问题:

I want to generate java classes through XSDs, I want to use/include one XSD file in another but when I include them in another XSDs same java class is generated in both the packages. I am also using maven-jaxb2-plugin plugin

回答1:

Do separate - aka modular - schema compilation using so-called episodes. That is to say, if you want to import schema A into schema B and generate classes for schema B, you first create a separate Maven project in order to compile schema A to a separate Maven artifact with maven-jaxb2-plugin as usual. (This is assuming the schema A stands alone, i.e. does not import any other schema; else you have to repeat the same process on the imported schema(s).) As a result, you get A.jar with the generated classes only from schema A, and most importantly, a META-INF/sun-jaxb.episode file. This file provides information about the existing XSD-to-Java bindings, and this will tell the maven-jaxb2-plugin (in fact the xjc tool underlying maven-jaxb2-plugin) what has already been generated from the schema, and therefore prevent it to re-generate the same classes again.

Then you create another Maven project in order to compile schema B, with maven artifact of A.jar among its Maven dependencies. This time, in the configuration of maven-jaxb2-plugin, you set the configuration parameter useDependenciesAsEpisodes to true. This will tell the plugin to use the .episode files from all dependencies (when there is any). You can find a basic example on the plugin's GitHub wiki. Below is a real-life example from AuthzForce project, an XACML implementation, where the OASIS XACML standard schema (xacml-core-v3-schema-wd-17.xsd) imports the W3C standard XML namespace schema (xml.xsd). Therefore, you have one Maven project/artifact for the xml.xsd, and another one for the XACML schema, where the relevant parts of the POM look like this:

<project ...>
  ...
  <dependencies>
    ...
    <dependency>
        <groupId>${project.groupId}</groupId>
        <artifactId>${artifactId.prefix}-xmlns-model</artifactId>
        <version>${project.parent.version}</version>
    </dependency>
    ...
  </dependencies>
  <build>
    <plugins>
        ...
        <plugin>
            <groupId>org.jvnet.jaxb2.maven2</groupId>
            <artifactId>maven-jaxb2-plugin</artifactId>
            <configuration>
                <verbose>true</verbose>
                <extension>true</extension>
                <strict>false</strict>
                <useDependenciesAsEpisodes>true</useDependenciesAsEpisodes>
                ...
                <catalog>src/main/jaxb/catalog.xml</catalog>
                <schemaDirectory>src/main/resources</schemaDirectory>
                <schemaIncludes>
                    <include>xacml-core-v3-schema-wd-17.xsd</include>
                </schemaIncludes>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
  </build>
</project>

You notice that there is also a catalog parameter pointing to the XML catalog file. This is also important because this catalog will enable the plugin to locate the A.xsd file (xml.xsd in my example) directly in its maven artifact, so that you don't need to duplicate the file in project B or elsewhere. In my example, since the XACML schema imports xml.xsd as follows:

<xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/>

... the catalog.xml must look like this:

<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
  <system systemId="http://www.w3.org/2001/xml.xsd" uri="maven:org.ow2.authzforce:authzforce-ce-xmlns-model:jar!/xml.xsd" />
</catalog>

You notice that the uri parameter references the xml.xsd from its Maven artifact. For more info on this syntax to refer to Maven artifact resources, refer to the maven-jaxb2-plugin's wiki.

In general, to allow maximum flexibility and simplicity in managing schema locations, I recommend to specify only the namespace in schema imports. (No schemaLocation.) For example, prefer this:

<xs:import namespace="http://www.w3.org/XML/1998/namespace" />

... in which case the catalog.xml should look like this:

<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
  <public publicId="http://www.w3.org/XML/1998/namespace" uri="maven:org.ow2.authzforce:authzforce-ce-xmlns-model:jar!/xml.xsd" />
</catalog>

(This is not the case in my example exceptionally, because the official XACML schema from the standard committee uses a schemaLocation, so it's preferable to keep it as is like the original.)