Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [eclipselink-users] XML Composite Object Mapping Problem

Hey Den,

The issue here is that you are attempting to map ResourceLocation in the context of a ResourceStatus, but these two objects aren't directly related in the object model.  The way it is currently mapped would indicate to EclipseLink that ResourceStatus has a composite relationship to ResourceLocation, which is not the case.  You can make use of our extended API to get this working, though.  Here's one way to do it:

Use an object to hold on to the ResourceStatus/ResourceLocation pairs, like this:
public class ResourceStatusLocations {
    public ResourceStatus rs;
    public ResourceLocation rl;
}

Setup a mapping for a list of these ResourceStatusLocations on the Response descriptor like so:
XMLCompositeCollectionMapping rslmapping = new XMLCompositeCollectionMapping();
rslmapping.setXPath("remc:ResourceStatusLocations");
rslmapping.setReferenceClass(ResourceStatusLocations.class);
rslmapping.setAttributeAccessor(new RSLAttributeAccessor());
rslmapping.setContainerPolicy(new ListContainerPolicy(ArrayList.class));

This list doesn't really  exist, i.e. there is no List<ResourseStatusLocations> attribute on Response, and that's okay.

Do not map List<ResourceStatus> and List<ResourceLocation> on the Response descriptor.

The mappings for the new ResourceStatusLocations would look like:
// mapping for ResourceStatus
XMLCompositeObjectMapping rsmapping = new XMLCompositeObjectMapping();
rsmapping.setAttributeName("rs");
rsmapping.setXPath(".");
rsmapping.setReferenceClass(ResourceStatus.class);

// mapping for ResourceLocation
XMLCompositeObjectMapping rlmapping = new XMLCompositeObjectMapping();
rlmapping.setAttributeName("rl");
rlmapping.setXPath("remc:Location");
rlmapping.setReferenceClass(ResourceLocation.class);

We will use a custom accessor that populates the ResourceStatusLocations objects based on List<ResourceStatus> and List<ResourceLocation> in the Response object (for marshalling), and populates List<ResourceStatus> and List<ResourceLocation> based on the ResourceStatusLocations objects during unmarshal.  Something like this:
public class RSLAttributeAccessor extends AttributeAccessor {

    @Override
    public Object getAttributeValueFromObject(Object object) throws DescriptorException {
        ArrayList<ResourceStatusLocations> rslList = new ArrayList<ResourceStatusLocations>();
        Response resp = (Response) object;

        // build a list of ResourceStatusLocations using the Response object's ResourceStatus and ResourceLocation lists
        for (int i=0; i<resp.rs.size(); i++) {
            rslList.add(new ResourceStatusLocations(resp.rs.get(i), resp.rl.get(i)));
        }
        return rslList;
    }

    @Override
    public void setAttributeValueInObject(Object object, Object value) throws DescriptorException {
        ArrayList<ResourceStatus> rsList = new ArrayList<ResourceStatus>();
        ArrayList<ResourceLocation> rlList = new ArrayList<ResourceLocation>();
       
        Response resp = (Response) object;
        ArrayList<ResourceStatusLocations> rslList = (ArrayList<ResourceStatusLocations>) value;

        // build ResourceStatus and ResourceLocation lists using the given ResourceStatusLocations
        for (ResourceStatusLocations rsls : rslList) {
            rsList.add(rsls.rs);
            rlList.add(rsls.rl);
        }
        resp.rs = rsList;
        resp.rl = rlList;
    }
   
    @Override
    public void initializeAttributes(Class descriptorClass) throws DescriptorException {
        // do nothing as the attribute doesn't really exist
    }
}

Using this configuration I can successfully unmarshal from/marshal to this instance document:
<?xml version="1.0" encoding="UTF-8"?>
<remc:Response xmlns:common="mycommonuri" xmlns:remc="myuri">
  <remc:ResourceStatusLocations>
    <remc:ID>AAA</remc:ID>
    <remc:Status>0</remc:Status>
    <remc:Location>
      <common:id>AAA</common:id>
      <common:Coordinate>
        <common:X>5.0</common:X>
    <common:Y>5.0</common:Y>
      </common:Coordinate>
    </remc:Location>
  </remc:ResourceStatusLocations>
  <remc:ResourceStatusLocations>
    <remc:ID>BBB</remc:ID>
    <remc:Status>2</remc:Status>
    <remc:Location>
      <common:id>BBB</common:id>
      <common:Coordinate>
        <common:X>7.0</common:X>
    <common:Y>7.0</common:Y>
      </common:Coordinate>
    </remc:Location>
  </remc:ResourceStatusLocations>
</remc:Response>

I've attached a test project so you can see exactly how I setup the mappings.  Please let me know if you have any questions.

--Dave



den.ty wrote:
Hi, 

I'm new to Eclipselink and I'm having trouble getting an XML mapping
working. 

I have the following Java classes:

    public class Response {
        List<ResourceStatus>  rs;
        List<ResourceLocation>  rl;    
        ...
    }

    public class ResourceStatus {
        private String id;
        private String status;
        ...
    }

    public class ResourceLocation {
        private String id;
        private Point posn;
        ...
    }

My XML doc is:

    <remc:Response> 

      <remc:ResourceStatusLocations> 
        <remc:ID>AAA</remc:ID>
        <remc:Status>0</remc:Status>  
        <remc:Location>                 
          <common:Coordinate>                
            <common:X>5.0</common:X>   
            <common:y>5.0</common:y> 
          </common:Coordinate>
        </remc:Location>
      </remc:ResourceStatusLocations>

      <remc:ResourceStatusLocations>          
        <remc:ID>BBB</remc:ID>
        <remc:Status>2</remc:Status>   
        <remc:Location>                 
          <common:Coordinate>                  
            <common:X>7.0</common:X>   
            <common:y>7.0</common:y> 
          </remc:Coordinate>
        </remc:Location>
      </remc:ResourceStatusLocations>

    </remc:Response>

and the equivalent objects should be 

    Response
        rl  ["AAA", (5.0, 5.0)], ["BBB", (7.0, 7.0)]

        rs  ["AAA", "0"],    ["BBB", "2"]

    
In workbench (EclipseLink Workbench v2.0.2), I have:

      Descriptor Response               --> context =
schema::nnn//element::remc:Response

            - Attribute ResourceLocation mapped as a composite collection:
                - xpath                = remc:ResourceStatusLocation
                - Reference descriptor = ResourceLocation

      Desciptor ResourceLocation        --> context =
schema::nnn//complexType:remc:ResourceStatusLocation

            - Attribute coordinate mapped as composite object using
customiser code
            
                final XMLCompositeObjectMapping mapping = new
XMLCompositeObjectMapping();
                mapping.setAttributeName("coordinate");
                mapping.setXPath("remc:Location/common:Coordinate");
                mapping.setReferenceClass(Point.class);
                descriptor.addMapping(mapping);            
     
 However, when marshalling the object to XML, I've got the id and statuses
mapped ok but the locations are missing:. 
 
     <remc:Response>
 
        <remc:ResourceStatusLocations>
           <remc:Status>0</remc:Status>
           <remc:ID>AAA</remc:ID>
        </remc:ResourceStatusLocations>
 
        <remc:ResourceStatusLocations>
           <remc:Status>2</remc:Status>
           <remc:ID>BBB</remc:ID>
        </remc:ResourceStatusLocations>
 
     </remc:Response>

 I have verified that the customiser code is being called and that the
Point.class matches the Coordinate element.
 
Is this the right approach to take or am I missing something obvious here?  
 
Thanks
Den
  

--
Oracle
David McCann | Principal Software Engineer | +6132884636
Oracle Server Technologies, EclipseLink Product
ORACLE Canada | 45 O'Connor St., Suite 400 | Ottawa, Ontario | K1P 1A4
Green Oracle Oracle is committed to developing practices and products that help protect the environment

Attachment: Coordinates.zip
Description: Zip compressed data


Back to the top