Question

I have two top-level classes which share a third class by composition. Example:

@XmlRootElement
@XmlType(namespace = "http://example.com/foo")
public class Foo {
    public Shared shared;
}

@XmlRootElement
@XmlType(namespace = "http://example.com/bar")
public class Bar {
    public Shared shared;
}

public class Shared {
    public String string;
}

Each of these classes is assigned to a different package in a different compilation unit (module). Now when I use schemagen on each top level class, I would like the Shared class to have the same name space than the top level class. So the output for Foo should look like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="foo" type="Foo"/>

  <xs:complexType name="Foo">
    <xs:sequence>
      <xs:element name="shared" type="Shared" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="Shared">
    <xs:sequence>
      <xs:element name="string" type="xs:string" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

However, it doesn't work like this. Instead the Shared class has the default namespace and so I get two schema files, one for the namespace of Foo and one for the namespace of Shared.

Is there a way to fix this without the obvious solution to duplicate the Shared class and thus, not sharing it anymore?

Was it helpful?

Solution 2

After investigating the issue and elaborating the options, I have decided to change the design and use one and only one namespace for all my classes. Here's the rationale:

  1. The relationship between a class and a namespace is naturally one to one. Even if I manually mock-up an XSD so that my instance document properly validates, the JAXB unmarshaller ignores elements which belong to a name space which doesn't match, so hacking the XSD doesn't help at all.

  2. I can easily reuse the same namespace over multiple packages and compilation units. The class path just needs to be set up correctly, which Maven will do for me when using the jaxb2-maven-plugin.

  3. I can easily associate all my classes with one namespace and still have different XSD files for each top-level element. The advantage is that each output XSD file contains only the classes (i.e. complex types) which are referenced from the included root element classes when running schemagen.

After making this change, I get one XSD file per compilation unit. Each compilation unit contains exactly one root element (i.e. class). Each root element references only the complex types which are strongly reachable through the class representing the root element. This is exactly what I wanted.

OTHER TIPS

If the shared class should have the same name space as the top level class you have to duplicate it. If you really want to share it, it must be defined in a third XSD and imported into the two top-level XSDs. So the result for Foo should rather look like:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://www.example.org/schema/foo"
    xmlns:shared="http://www.example.org/schema/shared"
    targetNamespace="http://www.example.org/schema/foo">

    <xs:import namespace="http://www.example.org/schema/shared" schemaLocation="http://www.example.org/schema/shared/shared.xsd"/>

    <xs:element name="foo" type="Foo"/>

    <xs:complexType name="Foo">
        <xs:sequence>
            <xs:element name="shared" type="shared:Shared" minOccurs="0"/>
        </xs:sequence>
    </xs:complexType>

</xs:schema>

The XSD for Shared can then be imported into both top level XSDs. If your Shared XSD is located in a separate project you might need a catalog file to specify its location:

PUBLIC "http://www.example.org/schema/shared" "../../../../shared/src/main/xsd/shared.xsd"

I am always using contract-first, i.e. create the XSD and let JAXB generate the classes. You are using schemagen to generate the XSD based on the classes. Just in case you are using the jaxb2-maven-plugin I found a Parameter transformSchemas which sounds like it is doing what you need. It lets you specify schema mappings and writes xs:import statements into the resulting XSD. I did not try it, but I hope it helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top