Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » EclipseLink » MOXy - xlink reference to previously marshalled objects
MOXy - xlink reference to previously marshalled objects [message #661234] Wed, 23 March 2011 16:08 Go to next message
Matti Hansson is currently offline Matti Hansson
Messages: 68
Registered: July 2009
Member
Hi!
I'm making a service that returns an Owner. The Owner can own Land and Buildings and each Building stands on a piece of Land, so the XML tree looks something like this:
Owner
  Land
  Land
  Building
    Land
  Building
    Land

Now, many Buildings may stand on the same piece of Land and the Land may or may not be owned by the Owner. I don't want to have several instances of the same Land object in my response. Instead, I'd like it if only the first instance was marshalled in full and the rest was only an xlink reference to the first.

Is this possible?

[Updated on: Thu, 24 March 2011 13:46]

Report message to a moderator

Re: MOXy - xlink reference to previously marshalled objects [message #661615 is a reply to message #661234] Fri, 25 March 2011 15:07 Go to previous messageGo to next message
Blaise Doughan is currently offline Blaise Doughan
Messages: 163
Registered: July 2009
Senior Member

You could leverage @XmlID/@XmlIDREF to do the following:

Land

Land will need an id property annotated with @XmlID:

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlID;

public class Land {

    private String id;

    @XmlID
    @XmlAttribute
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

}


Building

The land property on building will be annotated with @XmlIDREF:

import javax.xml.bind.annotation.XmlIDREF;

public class Building {

    private Land land;

    @XmlIDREF
    public Land getLand() {
        return land;
    }

    public void setLand(Land land) {
        this.land = land;
    }

}


B]Owner[/B]

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement
@XmlType(propOrder={"building", "land"})
public class Owner {

    private List<Land> land = new ArrayList<Land>();
    private List<Building> building = new ArrayList<Building>();

    public List<Land> getLand() {
        return land;
    }

    public void setLand(List<Land> land) {
        this.land = land;
    }

    public List<Building> getBuilding() {
        return building;
    }

    public void setBuilding(List<Building> building) {
        this.building = building;
    }

}


Demo

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        Owner owner = new Owner();

        Land land1 = new Land();
        land1.setId("1");
        Building building1 = new Building();
        building1.setLand(land1);
        owner.getBuilding().add(building1);
        owner.getLand().add(land1);

        Land land2 = new Land();
        land2.setId("2");
        Building building2 = new Building();
        building2.setLand(land2);
        owner.getBuilding().add(building2);
        owner.getLand().add(land2);

        JAXBContext jc = JAXBContext.newInstance(Owner.class);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(owner, System.out);
    }

}


Output

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<owner>
    <building>
        <land>1</land>
    </building>
    <building>
        <land>2</land>
    </building>
    <land id="1"/>
    <land id="2"/>
</owner>


-Blaise
Re: MOXy - xlink reference to previously marshalled objects [message #661641 is a reply to message #661615] Fri, 25 March 2011 16:26 Go to previous messageGo to next message
Matti Hansson is currently offline Matti Hansson
Messages: 68
Registered: July 2009
Member
Thanks a lot for your very detailed reply, Blaise! However, there is a problem:
Quote:
the Land may or may not be owned by the Owner

To illustrate, let's add a label to the land:
    private String label;

    @XmlElement
    public String getLabel() {
      return label;
    }

    public void setLabel(String label) {
      this.label = label;
    }

And make it so the Owner does not own land2:
    Owner owner = new Owner();

    Land land1 = new Land();
    land1.setId("1");
    land1.setLabel("land 1");
    Building building1 = new Building();
    building1.setLand(land1);
    owner.getBuilding().add(building1);
    owner.getLand().add(land1);

    Land land2 = new Land();
    land2.setId("2");
    land2.setLabel("land 2");
    Building building2 = new Building();
    building2.setLand(land2);
    owner.getBuilding().add(building2);
    //owner.getLand().add(land2);

We get the following output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<owner>
    <building>
        <land>1</land>
    </building>
    <building>
        <land>2</land>
    </building>
    <land id="1">
        <label>land 1</label>
    </land>
</owner>

As you can see, it's impossible to know the label of land2. I need every Land object to be fully marshalled exactly once. Is there a solution for this?
Re: MOXy - xlink reference to previously marshalled objects [message #661661 is a reply to message #661234] Fri, 25 March 2011 20:11 Go to previous messageGo to next message
Blaise Doughan is currently offline Blaise Doughan
Messages: 163
Registered: July 2009
Senior Member

Hello Again,

I apologize for missing that part of your question. The following will give you the type of behaviour you are looking for:

Land

Nothing special needs to be done for the Land class:

public class Land {

    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

}


Building

The land property on Building is where all the magic happens. We will use an XmlAdapter on this property.

import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

public class Building {

    private Land land;

    @XmlJavaTypeAdapter(MarshalAdapter.class)
    public Land getLand() {
        return land;
    }

    public void setLand(Land land) {
        this.land = land;
    }

}


MarshalAdapter

This is what the XmlAdapter will look like. This XmlAdapter requires the land list from the Owner object be set on it. You will see how this is done in the code for Demo.

import java.util.List;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class MarshalAdapter extends XmlAdapter<AdaptedLand, Land> {

    private List<Land> ownersLand;

    public MarshalAdapter() {
    }

    public MarshalAdapter(List<Land> ownersLand) {
        this.ownersLand = ownersLand;
    }

    @Override
    public Land unmarshal(AdaptedLand v) throws Exception {
        return null;
    }

    @Override
    public AdaptedLand marshal(Land v) throws Exception {
        AdaptedLand adaptedLand = new AdaptedLand();
        if(ownersLand.contains(v)) {
            adaptedLand.setOwnerLandIndex(String.valueOf(ownersLand.indexOf(v)));
        } else {
            adaptedLand.setLand(v);
        }
        return adaptedLand;
    }

}


AdaptedLand

This class is leveraged by the XmlAdapter:

import javax.xml.bind.annotation.XmlAttribute;
import org.eclipse.persistence.oxm.annotations.XmlPath;

public class AdaptedLand {

    private Land land;
    private String ownerLandIndex;

    @XmlPath(".")
    public Land getLand() {
        return land;
    }

    public void setLand(Land land) {
        this.land = land;
    }

    @XmlAttribute
    public String getOwnerLandIndex() {
        return ownerLandIndex;
    }

    public void setOwnerLandIndex(String id) {
        this.ownerLandIndex = id;
    }

}


Owner

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement
@XmlType(propOrder={"building", "land"})
public class Owner {

    private List<Land> land = new ArrayList<Land>();
    private List<Building> building = new ArrayList<Building>();

    public List<Land> getLand() {
        return land;
    }

    public void setLand(List<Land> land) {
        this.land = land;
    }

    public List<Building> getBuilding() {
        return building;
    }

    public void setBuilding(List<Building> building) {
        this.building = building;
    }

}


Demo

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        Owner owner = new Owner();

        Land land1 = new Land();
        land1.setAddress("123 Any Street");
        Building building1 = new Building();
        building1.setLand(land1);
        owner.getBuilding().add(building1);
        owner.getLand().add(land1);

        Land land2 = new Land();
        land2.setAddress("456 Another Road");
        Building building2 = new Building();
        building2.setLand(land2);
        owner.getBuilding().add(building2);
        //owner.getLand().add(land2);

        JAXBContext jc = JAXBContext.newInstance(Owner.class);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setAdapter(new MarshalAdapter(owner.getLand()));
        marshaller.marshal(owner, System.out);
    }

}


Output

<?xml version="1.0" encoding="UTF-8"?>
<owner>
   <building>
      <land ownerLandIndex="0"/>
   </building>
   <building>
      <land>
         <address>456 Another Road</address>
      </land>
   </building>
   <land>
      <address>123 Any Street</address>
   </land>
</owner>


-Blaise
Re: MOXy - xlink reference to previously marshalled objects [message #661871 is a reply to message #661661] Mon, 28 March 2011 11:25 Go to previous message
Matti Hansson is currently offline Matti Hansson
Messages: 68
Registered: July 2009
Member
Heh, no need to apologize. Your example is exactly what I need. Very thorough and easy to follow. Thanks, Blaise!
Previous Topic:Some questions about logging
Next Topic:Toplink to eclipselink upgrade issue
Goto Forum:
  


Current Time: Wed Oct 22 04:52:49 GMT 2014

Powered by FUDForum. Page generated in 0.08404 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software