Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAXB Mapping cyclic references to XML

I have an object graph that contains a cycle. How do I get JAXB to handle this? I tried using the @XmlTransient annotation in the child class but the JAXB marshaller still detects the cycle.

@Entity
@XmlRootElement
public class Contact {

    @Id
    private Long contactId;

    @OneToMany(mappedBy = "contact")
    private List<ContactAddress> addresses;

...

}

@Entity
@XmlRootElement
public class ContactAddress {

    @Id
    private Long contactAddressId;

    @ManyToOne
    @JoinColumn(name = "contact_id")
    private Contact contact;

    private String address;

...

}
like image 340
Jordan Allan Avatar asked Jun 18 '10 22:06

Jordan Allan


4 Answers

This page in the "Unofficial JAXB Guide" offers three strategies for dealing with cycles. They are (in summary):

  • Mark one of the reference attributes that form the cycle as @XmlTransient.
  • Use @XmlID and @XmlIDREF so that the references are represented using XML ids arather than by containment.
  • Use the CycleRecoverable interface to deal with cycles programmatically.
like image 117
Stephen C Avatar answered Oct 16 '22 21:10

Stephen C


The good thing about using JAXB is that it is a standard runtime with multiple implementations (just like JPA).

If you use EclipseLink JAXB (MOXy) then you have many extensions available to you for handling JPA entities including bi-directional relationships. This is done using the MOXy @XmlInverseReference annotation. It acts similar to @XmlTransient on the marshal and populates the target-to-source relationship on the unmarshal.

http://wiki.eclipse.org/EclipseLink/Examples/MOXy/JPA/Relationships

@Entity 
@XmlRootElement 
public class Contact { 

    @Id 
    private Long contactId; 

    @OneToMany(mappedBy = "contact") 
    private List<ContactAddress> addresses; 

... 

} 

@Entity 
@XmlRootElement 
public class ContactAddress { 

    @Id 
    private Long contactAddressId; 

    @ManyToOne 
    @JoinColumn(name = "contact_id") 
    @XmlInverseReference(mappedBy="addresses")
    private Contact contact; 

    private String address; 

... 

} 

Other extensions are available including support for composite keys & embedded key classes.

To specify the EcliseLink MOXy JAXB implementation you need to include a jaxb.properties file in with your model classes (i.e. Contract) with the following entry:

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
like image 35
bdoughan Avatar answered Oct 16 '22 21:10

bdoughan


XMLTransient almost always works for cycles. It might be a possibility that you have XMLTransient on the field level but you have not specified XMLAccessorType to be XmlAccessType.Field. If you don't specify anything the default is XmlAccessType.Property - or your getters. I have experienced Jaxb picking xml elements from getters from a class that I missed the accessor type annotations on and still work perfectly fine.

like image 6
Gunjan Kalra Avatar answered Oct 16 '22 21:10

Gunjan Kalra


just look at this tutorial : Mapping cyclic references to XML by jaxb

I use it an it works well :)

like image 1
LE GALL Benoît Avatar answered Oct 16 '22 21:10

LE GALL Benoît