users@jaxb.java.net

Re: derived fields

From: Wolfgang Laun <wolfgang.laun_at_gmail.com>
Date: Sat, 14 Feb 2009 11:50:59 +0100

Customizing the binding of a schema type to some Java type
using JAXB's external binding is not a "trivial exercise".
(I don't know RELAX NG - perhaps there is a way to produce
the XML schema code that enables xjc to generate an enum
type. And writing the XML Schema by hand might be preferable
in the long run.)

Here's what you'll have to do. - Note that I'm using
other names than you.

(1) Implement the enum impl.ImplType.
(2) Implement a class impl.ImplConv converting between
    String and the enum impl.ImplType.
(3) Add the customization to the bindings file signal.xjb.
(4) Generate code from the schema and bindings.

I'm assuming an XML Schema signal.xsd similar to this:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:complexType name="VariableType">
  <xsd:sequence>
    <xsd:element name="Value" type="xsd:int"/>
  </xsd:sequence>
  <xsd:attribute name="Impl" type="xsd:string"/>
</xsd:complexType>

<xsd:complexType name="MemoryType">
  <xsd:sequence>
    <xsd:element name="variable" type="VariableType"
                 maxOccurs="unbounded"/>
  </xsd:sequence>
</xsd:complexType>

<xsd:element name="Memory" type="MemoryType"/>

</xsd:schema>


Here are the details:

// (1)
package impl;

public enum ImplType {
  UINT8(1,false), INT8(1,true), UINT16(2,false), INT16(2,true),
  UINT32(4,false), INT32(4,true);
  private final int bytelength;
  private final boolean signed;
  ImplType(int bytelength, boolean signed) {
     this.bytelength = bytelength;
     this.signed = signed;
  }
  public static ImplType fromString( String s ){
      return valueOf( ImplType.class, s );
  }
}

// (2)
package impl;

import java.util.HashMap;
import java.util.Map;
public class ImplConv {
    public static ImplType parseStringToEnum( String value ){
        return ImplType.fromString( value );
    }
    public static String printEnumToString( ImplType impl ){
        return impl.toString();
    }
}

<!-- (3) signal.xjb -->
<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               jaxb:version="1.0">
  <jaxb:bindings schemaLocation="signal.xsd" node="/xsd:schema">
    <jaxb:schemaBindings>
      <jaxb:package name="signal"/>
    </jaxb:schemaBindings>
    <jaxb:bindings node="//xsd:complexType[@name='VariableType']">
      <jaxb:bindings node="./xsd:attribute[@name='Impl']">
        <jaxb:property>
          <jaxb:baseType>
            <jaxb:javaType name="impl.ImplType"
                           parseMethod="impl.ImplConv.parseStringToEnum"
                           printMethod="impl.ImplConv.printEnumToString"/>
          </jaxb:baseType>
        </jaxb:property>
      </jaxb:bindings>
    </jaxb:bindings>
  </jaxb:bindings>
</jaxb:bindings>

Notice how the <property> is associated with the attribute of the
complex type, using XPATH. The <javaType> must specify the Java type
that is to be bound to the attribute's schema type, and the parse
and print methods duing the actual conversion.

(4) xjc -b signal.xjb signal.xsd

Here's some sample code composing a content tree:

        ObjectFactory of = new ObjectFactory();

        MemoryType memory = of.createMemoryType();
        VariableType var1 = of.createVariableType();
        var1.setValue( 42 );
        var1.setImpl( ImplType.INT16 );
        memory.getVariable().add( var1 );
        VariableType var2 = of.createVariableType();
        var2.setValue( 1234567 );
        var2.setImpl( ImplType.UINT32 );
        memory.getVariable().add( var2 );

        JAXBElement<MemoryType> jbel = of.createMemory( memory );

And that's the output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Memory>
    <variable Impl="INT16">
        <Value>42</Value>
    </variable>
    <variable Impl="UINT32">
        <Value>1234567</Value>
    </variable>
</Memory>

Cheers
Wolfgang

On Fri, Feb 13, 2009 at 5:18 PM, Jason Sachs <jmsachs_at_gmail.com> wrote:

> I have a fairly simple schema used to autogenerate a few Java classes. So
> far it works fine. I would like to add some functionality on the Java side,
> however, and would like to get a recommendation for how to best do this.
>
> My schema contains an element <signal> that I am mapping to a Java class
> ESignal. (using jaxb:nameXmlTransform which works fine) It has an attribute
> called "type" that is a choice of the following 6 values: uint8, int8,
> uint16, int16, uint32, int32. The autogenerated ESignal class contains the
> method public String getType(). This is fine but I would like to somehow add
> or modify this to something like
>
> public enum SignalType {
> UINT8(1,false), INT8(1,true), UINT16(2,false), INT16(2,true),
> UINT32(4,false), INT32(4,true);
>
> final int bytelength;
> final boolean signed;
> public SignalType(bytelength, signed) { this.bytelength = bytelength;
> this.signed = signed; }
> public static SignalType fromString(String s)
> { ... exercise for the reader to convert from string to one of the enums
> ... }
> }
>
> // ... then in ESignal:
> public SignalType getType() { ... somehow this is supposed to return the
> proper enum. }
>
> Since the java class is autogenerated from the .xsd file (which I generate
> from a RELAX NG .rng file) and the .xjb file, I am not quite sure how to
> graft hand-coded methods onto the autogenerated class...
>
> This seems like a fairly trivial exercise but I am not sure. Any advice?
>
> --Jason
>