views:

3170

answers:

6

I'm currently using JAXB to generate java classes in order to unmarshall XML. Now I would like to create a new schema very similar to the first and have the classes that are generated implement the same interface.

Say for example, I have two schema files which define XML with similar tags:

adult.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:element name="Person">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="Name" type="xs:string" />
      <xs:element name="Age" type="xs:integer" />
      <xs:element name="Job" type="xs:string" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

kid.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:element name="Person">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="Name" type="xs:string" />
      <xs:element name="Age" type="xs:integer" />
      <xs:element name="School" type="xs:string" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

Using JAXB and XJC I'd like to generate two class files:

public class Adult implements Person {
    ...
    public String getName() { ... }
    public int getAge() { ... }
    public String getJob { ... }
}

public class Kid implements Person {
    ...
    public String getName() { ... }
    public int getAge() { ... }
    public String getSchool { ... }
}

where the Person interface defines the getName() and getAge() methods.

I've looked at some of the documentation for mapping interfaces but this appears to only be for the situation when you already have java classes that you want to map to a DOM.

Also, I've tried to use this external plugin but it doesn't appear to work. Here is my xjb binding file:

<jxb:bindings version="1.0" 
  xmlns:jxb="http://java.sun.com/xml/ns/jaxb" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
  xmlns:ext="http://xml.w-wins.com/xjc-plugins/interfaces"
  jxb:extensionBindingPrefixes="xjc">

    <jxb:bindings schemaLocation="xsd/adult.xsd" node="xs:schema/xs:complexType[@name='Person']">
        <ext:interface>mypackage.Hello</ext:interface> 
    </jxb:bindings>

</jxb:bindings>

but this gives the following error:

$ java -cp "lib/activation.jar;lib/InterfacesXJCPlugin.jar;lib/jaxb1-impl.jar;lib/jaxb-api.jar;lib/jaxb-xjc.jar;lib/jsr173_1.0_api.jar" com.sun.tools.xjc.XJCFacade -p mypackage.myxml -extension -Xinterfaces xsd/adult.xsd -b binding.xjb
parsing a schema...
[ERROR] XPath evaluation of "xs:schema/xs:complexType[@name='Person']" results in empty target node
  line 8 of file:/C:/dev/jaxb/jaxb-ri/binding.xjb

Failed to parse a schema.

Is it possible to generate a class with JAXB that implements an interface?

Update

I've tried using the Interface Insertion plugin but for some reason can't get it to work. This is how I'm calling xjc yet it is as if the plugin jar is not getting picked up from the classpath:

$ java -cp "lib/xjc-if-ins.jar;lib/jaxb-xjc.jar" com.sun.tools.xjc.XJCFacade -p mypackage -extension -Xifins myschema.xsd -b binding.xjb

I get the error:

unrecognized parameter -Xifins

Any ideas?

A: 

An XJC plygin of some description is the answer to your problem, it's just a matter of finding one that works. The best source for them is here:

https://jaxb2-commons.dev.java.net/

In particular, this one:

https://jaxb2-commons.dev.java.net/interface-insertion/

skaffman
A: 

The documentation for the interface-insertion plugin suggests the the following

[ To call xjc with the Interface Insertion Plugin from the command line, you may write:

java -cp $JAXB_HOME/share/lib/xjc-if-ins.jar -extension -Xifins schema

]

I am guessing that you are calling the main method of the wrong class - com.sun.tools.xjc.XJCFacade. You should probably retry with the exact syntax.

Here is a link on another forum which discusses a similar issue. http://forums.java.net/jive/message.jspa?messageID=220686

  • I would have posted this as a comment, but I do not have enough points to comment.
Thimmayya
+2  A: 

It might be overkill for your situation, but I have done this using AspectJ (we were already using aspects on that project so we already had the dependency and the exposure).

You'd declare an aspect along the lines of:

public aspect MyAspect
{
    declare parents: 
        com.foo.generated.Adult
    implements com.foo.Person;

    declare parents: 
        com.foo.generated.Kid
    implements com.foo.Person;
}

Which will add the interface com.foo.Person to the classes com.foo.generated.Adult and com.foo.generated.Adult

Might be overkill for your purpose, but it worked for us.

Chris
A: 

What if you extract the type into a common XSD imported into adult.xsd and kid.xsd? Or do you have an existing interface you need them to implement?

Mirvnillith
Unfortutnately, I cannot modify the XSDs. I'd hope to do this purely with a JAXB binding or plugin so that no changes need to be made to the XSDs.
Alex Spurling
A: 

Alex Spurling, Did you manage to get around the "unrecognized parameter -Xifins" issue

Kanna
No, I'm afraid I gave up on the idea...! :(
Alex Spurling
A: 

In my case the command-line call via java -jar works:

java -jar $somepath/jaxb-xjc.jar -classpath $somepath/xjc-if-ins.jar my.xsd -d $destdir -b $bindingconfig -p $desiredpackage -extension -Xifins

However when executing the xjc ant-task the error remains. The error message given is irritating as the real reason in my case is a bad version number in a class file ant tries to load (see stacktrace below). This correct error message only appears when you add the following to ANT_OPTS: -Dcom.sun.tools.xjc.Options.findServices=true

[xjc] java.lang.UnsupportedClassVersionError: Bad version number in .class file [xjc] at java.lang.ClassLoader.defineClass1(Native Method) [xjc] at java.lang.ClassLoader.defineClass(ClassLoader.java:620) [xjc] at org.apache.tools.ant.AntClassLoader.defineClassFromData(AntClassLoader.java:1134) [xjc] at org.apache.tools.ant.AntClassLoader.getClassFromStream(AntClassLoader.java:1320) [xjc] at org.apache.tools.ant.AntClassLoader.findClassInComponents(AntClassLoader.java:1376) [xjc] at org.apache.tools.ant.AntClassLoader.findClass(AntClassLoader.java:1336) [xjc] at org.apache.tools.ant.AntClassLoader.loadClass(AntClassLoader.java:1074) [xjc] at java.lang.ClassLoader.loadClass(ClassLoader.java:299) [xjc] at java.lang.ClassLoader.loadClass(ClassLoader.java:251) [xjc] at com.sun.tools.xjc.Options.findServices(Options.java:936) [xjc] at com.sun.tools.xjc.Options.getAllPlugins(Options.java:336) [xjc] at com.sun.tools.xjc.Options.parseArgument(Options.java:632) [xjc] at com.sun.tools.xjc.Options.parseArguments(Options.java:742) [xjc] at com.sun.tools.xjc.XJC2Task._doXJC(XJC2Task.java:444) [xjc] at com.sun.tools.xjc.XJC2Task.doXJC(XJC2Task.java:434) [xjc] at com.sun.tools.xjc.XJC2Task.execute(XJC2Task.java:369) [xjc] at com.sun.istack.tools.ProtectedTask.execute(ProtectedTask.java:55) [xjc] at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291) [xjc] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [xjc] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) [xjc] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [xjc] at java.lang.reflect.Method.invoke(Method.java:585) [xjc] at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106) [xjc] at org.apache.tools.ant.Task.perform(Task.java:348) [xjc] at org.apache.tools.ant.Target.execute(Target.java:390) [xjc] at org.apache.tools.ant.Target.performTasks(Target.java:411) [xjc] at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1360) [xjc] at org.apache.tools.ant.Project.executeTarget(Project.java:1329) [xjc] at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41) [xjc] at org.apache.tools.ant.Project.executeTargets(Project.java:1212) [xjc] at org.apache.tools.ant.Main.runBuild(Main.java:801) [xjc] at org.apache.tools.ant.Main.startAnt(Main.java:218) [xjc] at org.apache.tools.ant.launch.Launcher.run(Launcher.java:280) [xjc] at org.apache.tools.ant.launch.Launcher.main(Launcher.java:109) [xjc] [xjc] failure in the XJC task. Use the Ant -verbose switch for more details

cfsh