Community
Participate
Working Groups
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!
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; }
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; } }
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
(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
Sounds good, thanks! :) --Polly
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink