14

i would like to have bidirectional navigation methods in classes between child object and parent object. IDREF is not enough in my case because I don't want to specify le id of parent. to be clear, from an xsd like that:

<complexType name="A">
    <xs:sequence>
        <element name="b" type="B" minOccurs="0" maxOccurs="unbounded"></element>
    </xs:sequence>
    <attribute name="id" type="ID"></attribute>
</complexType>
<complexType name="B">
    <attribute name="id" type="ID"></attribute>
</complexType>

i would like classes looks like this:

class A {
    ...
    public List<B> getB() { ...}
    ...
   }
class B {
    ...
    public A getA() {
    ...
}

and my xml must looks like this:

<a id="a1">
    <b id="b1"/>
    <b id="b2"/>
    ...
</a>

After unmarshal, I would like to be able to navigate A to Bs AND from B to A (via b.getA()) !! It a very basic feature, but I don't find a simple way to achieve that ...

Any idea ??

Thanks in advance

fedevo
  • 631
  • 1
  • 7
  • 15
  • This might help you :http://stackoverflow.com/questions/5319024/using-jaxb-to-cross-reference-xmlids-from-two-xml-files – Cris Jun 14 '11 at 11:47

5 Answers5

15

You can create a method void afterUnmarshal(Unmarshaller, Object parent) that gets called... well, after unmarshalling and set up your object tree as you need it.

musiKk
  • 14,751
  • 4
  • 55
  • 82
  • However, how can I utilize this method if the classes are created from the XSD in every build run? I cannot modify the classes created by XJC. – migu Nov 29 '11 at 10:09
  • Now I understand: The javadoc comment of Unmarshaller is very misleading saying "'Class defined' event callback methods allow any JAXB mapped class to specify its own specific callback methods by defining methods with the following method signature." - Actually, the listener need not be a JAXB-mapped class! Just implement a listener class that does the work and register it to the unmarshaller instance. That's it. – migu Nov 29 '11 at 10:28
  • @migu: It's not exactly misleading. These are two distinct callback mechanisms. If you only need a single listener without access to private data of the unmarshalled classes, then sure `Unmarshaller.Listener` probably suffices. – musiKk Dec 07 '11 at 08:26
7

Note: I'm the EclipseLink JAXB (MOXy) lead, and a member of the JAXB (JSR-222) expert group.

Eclipse JAXB (MOXy) offers the @XmlInverseReference extension to handle this use case. Currently it can not be generated by XJC and must be applied to the domain model directly:

class A {
    ...
    public List<B> getB() { ...}
    ...
   }

class B {
    ...
    @XmlInverseReference(mappedBy="b")
    public A getA() {
    ...
}

For More Information

bdoughan
  • 147,609
  • 23
  • 300
  • 400
6

In addition to musiKk answer in case you are using xjc to generate domain model classes from xsd. To add reference to parent class in all model classes you should:

  1. Create base class which extends Unmarshaller.Listener

    public abstract class BaseClass extends Unmarshaller.Listener {
        protected Object parent;
    
        public void afterUnmarshal(Unmarshaller unmarshaller, Object parent)     {
            this.parent = parent;
        }
    
        public Object getParent(){
            return parent;
        }
    }
    
  2. Tell xjc that all model classes should extend your BaseClass by creating global xjc binding configuration

    <jaxb:globalBindings>
        <xjc:superClass name="com.acme.BaseClass" />
    </jaxb:globalBindings>
    
Abdul Saleem
  • 10,098
  • 5
  • 45
  • 45
swarmshine
  • 61
  • 1
  • 2
0
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">   
    <xs:complexType name="A">
    <xs:sequence>
        <xs:element name="b" type="B" minOccurs="0" maxOccurs="unbounded">
        </xs:element>
    </xs:sequence>
    <xs:attribute name="id" type="xs:ID"></xs:attribute>
</xs:complexType>
<xs:complexType name="B">
 <xs:sequence>
    <xs:element name="a" type="A" minOccurs="1" maxOccurs="1"/>
     </xs:sequence>
</xs:complexType>
 <xs:attribute name="id" type="xs:ID"></xs:attribute>
</xs:schema>

Try this...

Cris
  • 4,947
  • 6
  • 44
  • 73
0

To make swarmshine answer work with org.glassfish.jaxb:jaxb-runtime:jar:2.3.5, I made a few adjustments :

  • I had to annotate the BaseClass class and the parent attribute with @XmlTransient otherwise creating JAXBContext failed. It is because BaseClass is not defined in my XSD file
  • I did not need to extend Unmarshaller.Listener. I believe the runtime finds the afterUnmarshal method by itself, based on the name and the signature.
package mypackage;

import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlTransient;

@XmlTransient
public abstract class BaseClass {

    @XmlTransient
    private BaseClass parent;

    private void afterUnmarshal(Unmarshaller unmarshaller, Object parent){
        if (parent instanceof BaseClass){
            this.parent = (BaseClass) parent;
        }
    }

    public BaseClass getParent() {
        return this.parent;
    }
}
birou007
  • 1
  • 1