[
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
--
David McCann | Principal Software Engineer | +6132884636
Oracle Server Technologies, EclipseLink
Product
ORACLE Canada | 45 O'Connor St., Suite 400 | Ottawa, Ontario | K1P 1A4
|
Oracle is committed to developing practices and
products that help protect the environment |
|
Attachment:
Coordinates.zip
Description: Zip compressed data