Bug 298220 - Enhancement: overriding mapped xpaths for composite objects
Summary: Enhancement: overriding mapped xpaths for composite objects
Status: NEW
Alias: None
Product: z_Archived
Classification: Eclipse Foundation
Component: Eclipselink (show other bugs)
Version: unspecified   Edit
Hardware: PC Windows XP
: P3 enhancement (vote)
Target Milestone: ---   Edit
Assignee: Blaise Doughan CLA
QA Contact:
URL: http://old.nabble.com/-MOXy--overridi...
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-12-18 14:39 EST by Polly Chang CLA
Modified: 2022-06-09 10:14 EDT (History)
2 users (show)

See Also:


Attachments
Core - Fix (WIP) (2.02 KB, patch)
2010-02-09 14:35 EST, Blaise Doughan CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Polly Chang CLA 2009-12-18 14:39:11 EST
Original thread:  http://old.nabble.com/-MOXy--overriding-mapped-xpaths-to26836075.html

We have a situation like this:

<Foo>
    <A>...</A>
    <B>...</B>
    <C>...</C>
</Foo>

The domain model looks like:

public class MyBasicType {
    String fieldA;
    String fieldB;
}

public class Foo{
    MyBasicType myBasicType
    String fieldC;
}

Normally I'd map the xpaths for MyBasicType to elements "A" and "B" and map Foo's reference to MyBasicType using an XMLCompositeCollectionMapping.  But what happens if we want to reuse MyBasicType to map to this document:

<Bar>
    <Y>...</Y>
    <Z>...</Z>
    ...
</Bar>

public class Bar {
    MyBasicType myBasicType
    ...
}

This would require overriding the xpaths defined for MyBasicType's internal fields based on where MyBascType is used (Foo or Bar).  

Can you provide a way to map to this domain model?  Thanks!
Comment 1 Blaise Doughan CLA 2010-02-09 14:35:50 EST
Created attachment 158619 [details]
Core - Fix (WIP)

Possible Approach:

This would allow the reference descriptor to be set on composite mappings directly (instead of by class), therefore allowing a class to be mapped by multiple descriptors.

    private XMLDescriptor getCustomerDescriptor() {
        XMLDescriptor xmlDescriptor = new XMLDescriptor();
        xmlDescriptor.setJavaClass(Customer.class);
        xmlDescriptor.setDefaultRootElement("customer");

        XMLCompositeObjectMapping baMapping = new XMLCompositeObjectMapping();
        baMapping.setAttributeName("billingAddress");
        baMapping.setXPath("billing-address");
        baMapping.setReferenceDescriptor(getAddressDescriptor1());
        xmlDescriptor.addMapping(baMapping);

        XMLCompositeObjectMapping saMapping = new XMLCompositeObjectMapping();
        saMapping.setAttributeName("shippingAddress");
        saMapping.setXPath("shipping-address");
        saMapping.setReferenceDescriptor(getAddressDescriptor2());
        xmlDescriptor.addMapping(saMapping);

        return xmlDescriptor;
    }
Comment 2 Blaise Doughan CLA 2010-02-09 16:21:27 EST
Workaround:

Essentially you can create multiple descriptors for the same class, as long as you tell EclipseLink they belong to different classes.  In the example below 2 descriptors are created for Address.class, when they are added to the project one is assigned to Address.class, and the other String.class.  When you specify the reference descriptor on the composite object mapping, you need to specify the class that the descriptor is linked to on the project.

package multipledescriptors;

import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;
import org.eclipse.persistence.oxm.mappings.XMLDirectMapping;
import org.eclipse.persistence.sessions.Project;

public class CustomerProject extends Project {

    public CustomerProject() {
        this.addDescriptor(getAddressDescriptor1());
        this.getDescriptors().put(Integer.class, getAddressDescriptor2());
        this.addDescriptor(getCustomerDescriptor());
    }

    private XMLDescriptor getCustomerDescriptor() {
        XMLDescriptor xmlDescriptor = new XMLDescriptor();
        xmlDescriptor.setJavaClass(Customer.class);
        xmlDescriptor.setDefaultRootElement("customer");

        XMLCompositeObjectMapping baMapping = new XMLCompositeObjectMapping();
        baMapping.setAttributeName("billingAddress");
        baMapping.setXPath("billing-address");
        baMapping.setReferenceClass(Address.class);
        xmlDescriptor.addMapping(baMapping);

        XMLCompositeObjectMapping saMapping = new XMLCompositeObjectMapping();
        saMapping.setAttributeName("shippingAddress");
        saMapping.setXPath("shipping-address");
        saMapping.setReferenceClass(Integer.class);
        xmlDescriptor.addMapping(saMapping);

        return xmlDescriptor;
    }

    private XMLDescriptor getAddressDescriptor1() {
        XMLDescriptor address1Descriptor = new XMLDescriptor();
        address1Descriptor.setJavaClass(Address.class);

        XMLDirectMapping streetMapping = new XMLDirectMapping();
        streetMapping.setAttributeName("street");
        streetMapping.setXPath("street/text()");
        address1Descriptor.addMapping(streetMapping);

        return address1Descriptor;
    }

    private XMLDescriptor getAddressDescriptor2() {
        XMLDescriptor address2Descriptor = new XMLDescriptor();
        address2Descriptor.setJavaClass(Address.class);

        XMLDirectMapping streetMapping = new XMLDirectMapping();
        streetMapping.setAttributeName("street");
        streetMapping.setXPath("road/text()");
        address2Descriptor.addMapping(streetMapping);

        return address2Descriptor;
    }

}
Comment 3 Polly Chang CLA 2010-02-10 10:41:05 EST
Hi Blaise,

The "Possible Approach" looks very clean and easy to understand.  As long as the deployment project xml file or the new oxm mapping file also supports that (meaning I don't have to use the Java API), that would be great.  :)

The workaround is interesting also.  I was not aware that it is possible to trick EclipseLink that way.  ;)  I have a couple of questions about that.  So I am guessing that you added "addressDescriptor1" and "addressDescriptor2" to the project differently because the default addDescriptor() would put the descriptor in the map keyed by the descriptor's class name, but here we want to use another class as the key while still keeping the descriptor mapped to the right class.  This means that there is a limited number of times that I can use this trick right?  Because there are only so many valid classes I can use as the key for the duplicate mapping (String, Integer, Double, etc.).  It's an interesting workaround nonetheless.  I just want to confirm my understanding.

Thanks,
Polly
Comment 4 Blaise Doughan CLA 2010-02-10 10:55:51 EST
(In reply to comment #3)
Hi Polly,

For the "Possible Approach", we still need to work out how this would look in the externalized metadata.

For the "Workaround", you are correct in that the second descriptor was added to the project differently on purpose.  You could use any existing class for the trick (i.e. String.class, StringBuffer.class, RuntimeException.class, etc).

-Blaise
Comment 5 Polly Chang CLA 2010-02-10 10:58:37 EST
Sounds good, thanks!  :)

--Polly
Comment 6 Eclipse Webmaster CLA 2022-06-09 10:11:00 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink
Comment 7 Eclipse Webmaster CLA 2022-06-09 10:14:14 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink