reza
reza

Reputation: 1218

XML IDREF in JAXB returns the wrong class?

I am new to both XSD design and JAXB. I managed to create my XSD as below:

<xsd:element name="policy" >
<xsd:complexType>
    <xsd:sequence>
        <xsd:element name="actor-def" type="tns:actor-def"/>                        
        <xsd:element name="actor-system-def" type="tns:actor-system-def"/>           
        </xsd:sequence>
</xsd:complexType>
</xsd:element>

<xsd:complexType name="actor-def">
    <xsd:sequence>
        <xsd:element name="actor-system" type="tns:actor-system-type"/>
    </xsd:sequence>
    <xsd:attribute name="name" type="xsd:ID" use="required" />
</xsd:complexType>
<xsd:complexType name="actor-system-type">
    <xsd:attribute name="name" type="xsd:IDREF" use="required" />
</xsd:complexType>

<xsd:complexType name="actor-system-def">
    <xsd:attribute name="name" type="xsd:ID" use="required" />
    <xsd:attribute name="url" type="xsd:anyURI" use="required" />
</xsd:complexType>

Basically, I am having a root element of "policy" which allows several "actor-def" and "actor-system-def" to be defined. each "actor-def" will belong to one "actor-system-def" that is achieved by using ID and IDREF.

In order to use JAXB with this, I created all the required classes automatically using Eclipse's JAXB Classes for Schema creation tool. It creates the following classes automatically based on the XSD file:

Policy.java

ActorDef.java

ActorSystemDef.java

ActorSystemType.java

everything looks fine except for the ActorDef.java file and the type of the actorSystem variable in it which is defined using IDREF in the XSD file. Basically, ActorDef.java file has the following:

@XmlElement(name = "actor-system", required = true)
protected ActorSystemType actorSystem;
public ActorSystemType getActorSystem() {
    return actorSystem;
}

The problem with this is that since the returned type from the ActorDef.getActorSystem() is of type ActorSystemType, it is a reference to the wrong object. What I want is a reference to the related ActorSystemDef so that I can call the getName() method on that to get the name of the ActorSystemDef (as can be seen below)

JAXBContext myJAXBContext = JAXBContext.newInstance("spec_jaxb");
Unmarshaller myUnmarshaller = myJAXBContext.createUnmarshaller();
Policy myPolicy = (Policy) myUnmarshaller.unmarshal(new  File("src/main/java/spec/private_policy.xml"));

List<ActorDef> actorList = myPolicy.getActorDef();
for (ActorDef actor : actorList) {
    System.out.println(actor.getActorSystem().getName());
}

I was able to make the code work by manually modifying the classes that were created automatically by the JAXB tool. I changed the type of the actorSystem variable in the ActorDef.java to ActorSystemDef as below:

protected ActorSystemDef actorSystem;
public ActorSystemDef getActorSystem() {
    return actorSystem;
}

This solved the problem and the code works fine but I want to learn how I can correct my XSD file so that the above problem is automatically corrected by the JAXB class creation tool. I think I am making a mistake in my XSD definition that is causing this. I modify my XSD file very often and can't make all sort of these changes manually every time.

Upvotes: 1

Views: 1130

Answers (2)

reza
reza

Reputation: 1218

@acdcjunior: Following to my last comment, your provided solution will not allow a code like this:

  <tns:actor-def>
    <tns:name>actor1</tns:name>
    <tns:description>actor 1 desc</tns:description>
    <tns:type>local</tns:type>
    <tns:actor-reference>www.actor.com</tns:actor-reference>
    <tns:actor-system>
      <tns:name>actor-system-1</tns:name>
    </tns:actor-system>
  </tns:actor-def>
  <tns:actor-system-def>
    <tns:name>actor-system-1</tns:name>
    <tns:description>actor-system-1 desc</tns:description>
    <tns:url>http://actorsystem1.org</tns:url>
    <tns:port>80</tns:port>
  </tns:actor-system-def>

In fact, if the actor-system-1 is already defined, it doesn't allow referring to it. Instead, you have to create a new actor-system from within the actor-def.

I was able to solve the problem by changing your code as below:

<xsd:element name="policy">
<xsd:complexType>
    <xsd:sequence>
        <xsd:element name="actor-def" type="tns:actor-def"/>
        <xsd:element name="actor-system-def" type="tns:actor-system-def"/>
    </xsd:sequence>
</xsd:complexType>
</xsd:element>

<xsd:complexType name="actor-def">
    <xsd:sequence>
        <xsd:element name="actor-system" type="xsd:IDREF"/>
    </xsd:sequence>
    <xsd:attribute name="name" type="xsd:ID" use="required" />
</xsd:complexType>

<xsd:complexType name="actor-system-def">
    <xsd:attribute name="name" type="xsd:ID" use="required" />
    <xsd:attribute name="url" type="xsd:anyURI" use="required" />
</xsd:complexType>

Now when the JAXB classes are created, I only have the ActorDef.java and ActorSystemDef.java and the code for ActorDef.getActorSystem() looks like this:

@XmlElement(name = "actor-system", required = true)
@XmlIDREF
@XmlSchemaType(name = "IDREF")
protected Object actorSystem;    

public Object getActorSystem() {
        return actorSystem;
    }

which can easily be converted to a ActorSystemDef object using casting:

system.out.println(   ((ActorSystemDef) actor.getActorSystem()).getName()  );

Upvotes: 0

acdcjunior
acdcjunior

Reputation: 135762

I tried your schema, and the got the for the ActorDef class, the same as you:

@XmlElement(name = "actor-system", required = true)
protected ActorSystemType actorSystem;

public ActorSystemType getActorSystem() { return actorSystem; }
public void setActorSystem(ActorSystemType value) { this.actorSystem = value; }

But then I removed this part of you schema (as it seems you are not using it - why was it there?):

<xsd:complexType name="actor-system-type">
    <xsd:attribute name="name" type="xsd:IDREF" use="required" />
</xsd:complexType>

And then changed the actor-system element's type from:

<xsd:element name="actor-system" type="tns:actor-system-type"/>

To:

<xsd:element name="actor-system" type="tns:actor-system-def"/>

Thus, the ending schema becoming:

<xsd:element name="policy">
<xsd:complexType>
    <xsd:sequence>
        <xsd:element name="actor-def" type="tns:actor-def"/>
        <xsd:element name="actor-system-def" type="tns:actor-system-def"/>
    </xsd:sequence>
</xsd:complexType>
</xsd:element>

<xsd:complexType name="actor-def">
    <xsd:sequence>
        <xsd:element name="actor-system" type="tns:actor-system-def"/>
    </xsd:sequence>
    <xsd:attribute name="name" type="xsd:ID" use="required" />
</xsd:complexType>

<xsd:complexType name="actor-system-def">
    <xsd:attribute name="name" type="xsd:ID" use="required" />
    <xsd:attribute name="url" type="xsd:anyURI" use="required" />
</xsd:complexType>

Then using this one, I generated the classes and got, for ActorDef, this:

@XmlElement(name = "actor-system", required = true)
protected ActorSystemDef actorSystem;

public ActorSystemDef getActorSystem() { return actorSystem; }
public void setActorSystem(ActorSystemDef value) { this.actorSystem = value; }

Seems to me that's what you were expecting in the first place, right?

Also, as it should be, the ActorSystemType.java is no longer generated.

Upvotes: 1

Related Questions