I have two classes
class First{
private Date date;
public Date getDate(){
return date;
}
...
}
and
class Second extends First{
#XmlAttribute
#XmlJavaTypeAdapter(value = DateAdapter.class, type = Date.class)
public Date getDate() {
return super.getDate();
}
}
Where DateAdapter just translates Date to Long and back.
I'm serializing an instance of the Second class and it seems to be that DateAdapter is ignored. I mean I get string "2013-05-22T13:32:40.664" instead of its Long representation.
If I'll move the #XmlJavaTypeAdapter annotation to the First class, it works OK, but my problem is that First can't be modified and that is basically the reason I created the wrapper class Second.
How can I make XmlJavaTypeAdapter be recognized?
You can use EclipseLink JAXB (MOXy)'s external binding file to provide metadata for classes that cannot be modified:
oxm.xml
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="com.example.foo">
<java-types>
<java-type name="First">
<java-attributes>
<xml-element java-attribute="date">
<xml-java-type-adapter value="com.example.foo.DateAdapter"/>
</xml-element>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Demo
Then you can bootstrap your JAXBContext using a property specifying the metadata location:
import java.util.*;
import javax.xml.bind.JAXBContext;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "com/example/foo/oxm.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {First.class, Second.class}, properties);
}
}
For More Information
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
Related
I have a collection on my class that uses #XmlElementWrapper to wrap the collection in a extra element.
So, my class looks something like this:
class A {
#XmlElement(name = "bee")
#XmlElementWrapper
public List<B> bees;
}
And my XML then looks something like:
<a>
<bees>
<bee>...</bee>
<bee>...</bee>
</bees>
</a>
Great, this is what I wanted. However, when I try and marshall into JSON, I get this:
{
"bees": {
"bee": [
....
]
}
}
And I don't want that extra "bee" key there.
Is it possible to somehow have MOXy ignore the XmlElement part when doing this marshalling? because I still need the name to be "bees" and not "bee", and I don't want both.
I'm using MOXy 2.4.1 and javax.persistence 2.0.0.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
oxm.xml
You could use MOXy's external mapping document to provide an alternate mapping for your JSON-binding (see: http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html).
<?xml version="1.0"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum14002508">
<java-types>
<java-type name="A">
<java-attributes>
<xml-element java-attribute="bees" />
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Demo
In the demo code below we will create two instances of JAXBContext. The first is build solely on the JAXB annotations that we will use for XML. The second is built on the JAXB annotations and uses MOXy's external mapping file to override the mapping for the bees property on the A class.
package forum14002508;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
List<B> bees = new ArrayList<B>();
bees.add(new B());
bees.add(new B());
A a = new A();
a.bees = bees;
JAXBContext jc1 = JAXBContext.newInstance(A.class);
Marshaller marshaller1 = jc1.createMarshaller();
marshaller1.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller1.marshal(a, System.out);
Map<String, Object> properties = new HashMap<String, Object>(3);
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "forum14002508/oxm.xml");
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
JAXBContext jc2 = JAXBContext.newInstance(new Class[] {A.class}, properties);
Marshaller marshaller2 = jc2.createMarshaller();
marshaller2.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller2.marshal(a, System.out);
}
}
Output
Below is the output from running the demo code that matches your use case.
<a>
<bees>
<bee/>
<bee/>
</bees>
</a>
{
"bees" : [ {
}, {
} ]
}
For More Information
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
I have a quite simple question :
Say I have a model class defined like this :
public class Test{
private String testAttribute;
public Test(){
}
public String getFormattedTestAttribute(){
return testAttribute + "A nice formatted thingy"; //right, this is just an example
}
public void setTestAttribute(String value){
testAttribute = value;
}
}
You can see that I have a standard setter for testProperty but the getter has a different name : getFormattedTestProperty().
Is it possible into Jaxb/Moxy to specify which getter to use for a specific property ?
I'm using MOXy implementation with external metadata bindings file. The project which I'm working on used tu use Castor. Into Castor's mapping files, you could specify which getter/setter to use like that :
<field name="testAttribute"
get-method="getFormattedTestAttribute">
<bind-xml name="test-attribute" node="attribute"/>
</field>
Is the same kind of thing possible with moxy's external metadata ?
If that kind of customization isn't supported, is it possible to mark a field as read-only and another as write-only ? so I could declare a read-only property named "formattedTestAttribute" and a write-only property named "testAttribute" into the metadata bindings file ?
<!-- read only property -->
<xml-element java-attribute="formattedTestAttribute" xml-path="#test-attribute" />
<!-- write only property -->
<xml-element java-attribute="testAttribute" xml-path="#test-attribute" />
Please note that I have very limited control over the model classes.
Thanks in advance for your answers.
You could represent this in EclipseLink JAXB (MOXy)'s external mapping document as follows:
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum8834871">
<java-types>
<java-type name="Test" xml-accessor-type="PUBLIC_MEMBER">
<xml-root-element/>
<java-attributes>
<xml-element
java-attribute="testAttribute"
name="test-attribute">
<xml-access-methods
get-method="getFormattedTestAttribute"
set-method="setTestAttribute"/>
</xml-element>
<xml-transient java-attribute="formattedTestAttribute"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Test
I have modified your Test class, to put some logic in the get/set methods.
package forum8834871;
public class Test{
private String testAttribute;
public Test(){
}
public String getFormattedTestAttribute(){
return "APPENDED_ON_GET " + testAttribute;
}
public void setTestAttribute(String value){
testAttribute = "APPENDED_ON_SET " + value;
}
}
Demo
package forum8834871;
import java.io.File;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum8834871/oxm.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Test.class}, properties);
File xml = new File("src/forum8834871/input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
Test test = (Test) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(test, System.out);
}
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<test>
<test-attribute>ORIGINAL</test-attribute>
</test>
Output
<?xml version="1.0" encoding="UTF-8"?>
<test>
<test-attribute>APPENDED_ON_GET APPENDED_ON_SET ORIGINAL</test-attribute>
</test>
Once again I have a question about Eclipselink/MOXy with external metadata mapping file.
I have a reference xml which applies to a class. This xml contains data that applies to some but not always all the properties that the class can contain.
I also have a custom datetime adapter set for the date fields.
My problem is that the xml I'm unmarshalling does not contain any data for the endDate property, yet when I do this simple test :
Unmarshall reference xml to the class
Marshall that class to a new xml file
Compare the two xml files
That property endDate (which should not be marshalled since it has not been set) is marshalled as 09/01/2012 17:05:28 (it's always marshalled as a new Date() set to the current time).
Here is a sample XML Metadata file :
<?xml version="1.0"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
version="2.1">
<java-types>
<java-type name="sample.clazz.Task" xml-accessor-type="NONE">
<xml-root-element name="Task" />
<xml-type prop-order="startDate endDate id ci ch cr" />
<java-attributes>
<xml-element java-attribute="startDate" xml-path="StartDate/text()">
<xml-java-type-adapter value="utils.JaxBDateTimeAdapter" type="java.util.Date"/>
</xml-element>
<xml-element java-attribute="endDate" required="false" xml-path="EndDate/text()">
<xml-java-type-adapter value="utils.JaxBDateTimeAdapter" type="java.util.Date"/>
</xml-element>
<xml-element java-attribute="id" xml-path="TaskId/text()" />
<xml-element java-attribute="ci" xml-path="CIPR/text()" />
<xml-element java-attribute="ch" xml-path="CHPR/text()" />
<xml-element java-attribute="cr" xml-path="CRPR/text()" />
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Here is the class :
package sample.clazz;
public class Task{
private int id;
private Date startDate;
private Date endDate;
private String ci;
private String ch;
private String cr;
public Task(){
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public String getCi() {
return ci;
}
public void setCi(String ci) {
this.ci = ci;
}
public String getCh() {
return ch;
}
public void setCh(String ch) {
this.ch = ch;
}
public String getCr() {
return cr;
}
public void setCr(String cr) {
this.cr = cr;
}
}
Here is my custom DateTimeAdapter :
package utils;
import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class JaxBDateTimeAdapter extends XmlAdapter<String, Date> {
#Override
public String marshal(Date d) throws Exception {
if(d != null){
return DateUtil.getFormatedDateTimeString(d);
}
else{
return null;
}
}
#Override
public Date unmarshal(String d) throws Exception {
if(d != null && !"".equals(d)){
return DateUtil.getDateFromString(d);
}
else{
return null;
}
}
}
Here is my reference XML
<?xml version="1.0" encoding="UTF-8"?>
<Task>
<TaskId>147</TaskId>
<CRPR>0087</CRPR>
<CIPR>A683557</CIPR>
<CHPR>BV</CHPR>
<StartDate>22/01/2009 20:56:29</StartDate>
</Task>
and Here is the XML I'm getting when re-marshalling the object :
<?xml version="1.0" encoding="UTF-8"?>
<Task>
<TaskId>147</TaskId>
<CRPR>0087</CRPR>
<CIPR>A683557</CIPR>
<CHPR>BV</CHPR>
<StartDate>01/01/2012 20:56:29</StartDate>
<EndDate>09/01/2012 17:05:28</EndDate> <!-- That element should not exist ! -->
</Task>
It seems like Jaxb generates a new date for the empty field, how can I tell him via the external metadata mapping file not to generate nodes for empty or null values ? I tried to set required=false on the metadata file, and I tried testing with my custom DateTimeAdapter if the values were null, but it seems Jaxb creates a new Date object and passes it to the marshal method of the Adapter. I cant think of any way of preventing him to do this.
As for my previous questions, I have no control over the incoming XML's or the model classes.
Please note : this data is a sample I wrote, it may not be accurate since I cannot expose real data or names, there might be some typing errors.
Thanks for your help.
I'm the EclipseLink JAXB (MOXy) lead and I have not been able to reproduce your issue. It may be possible that there is a problem in your DateUtil class. The following is what I have tried:
oxm.xml
I made a small change to your metadatafile. Basically I changed it to specify the package name on the xml-bindings element rather than the individual java-type elements:
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
version="2.3"
package-name="sample.clazz">
<java-types>
<java-type name="Task" xml-accessor-type="NONE">
<xml-root-element name="Task" />
<xml-type prop-order="startDate endDate id ci ch cr" />
<java-attributes>
<xml-element java-attribute="startDate" xml-path="StartDate/text()">
<xml-java-type-adapter value="forum8791782.JaxBDateTimeAdapter" type="java.util.Date"/>
</xml-element>
<xml-element java-attribute="endDate" required="false" xml-path="EndDate/text()">
<xml-java-type-adapter value="forum8791782.JaxBDateTimeAdapter" type="java.util.Date"/>
</xml-element>
<xml-element java-attribute="id" xml-path="TaskId/text()" />
<xml-element java-attribute="ci" xml-path="CIPR/text()" />
<xml-element java-attribute="ch" xml-path="CHPR/text()" />
<xml-element java-attribute="cr" xml-path="CRPR/text()" />
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
DateUtil
You did not provide an implementation of DateUtil in your question, so I used the following. My guess is there is code in your implementation of DateUtil that is causing the output that you are seeing:
package forum8791782;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
private static SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
public static String getFormatedDateTimeString(Date d) {
return formatter.format(d);
}
public static Date getDateFromString(String d) {
try {
return formatter.parse(d);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
Demo
Below is the code I used to run this example. input.xml is the reference XML you cite in your question:
package forum8791782;
import java.io.File;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.Version;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
import sample.clazz.Task;
public class Demo {
public static void main(String[] args) throws Exception {
System.out.println(Version.getVersionString());
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum8791782/oxm.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Task.class}, properties);
File xml = new File("src/forum8791782/input.xml");
Unmarshaller u = jc.createUnmarshaller();
Task task = (Task) u.unmarshal(xml);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(task, System.out);
}
}
Output
The following is the output I get from running the sample code. I do not see the EndDate element written out.
2.3.2.v20111125-r10461
<?xml version="1.0" encoding="UTF-8"?>
<Task>
<StartDate>22/01/2009 20:56:29</StartDate>
<TaskId>147</TaskId>
<CIPR>A683557</CIPR>
<CHPR>BV</CHPR>
<CRPR>0087</CRPR>
</Task>
I am using the EclipseLink implementation (2.3) of JAXB to map POJOs to XML and encountering a problem with following usecase:
public abstract class A {
public abstract Set<X> getX();
// There is no setter
}
public class B extends A {
// Set via constructor
private Set<X> x;
#Override
public Set<X> getX();
}
I am defining the mapping itself completely in an external bindings-file, i set class A to be transient like so:
<java-type name="foo.A" xml-transient="true"/>
and for class B:
<java-type name="bar.B" xml-accessor-type="PROPERTY">
<xml-root-element name="B" />
<java-attributes>
<xml-element java-attribute="x" xml-path="..."/>
</java-attributes>
</java-type>
Now, upon marshalling i am getting the exception: "Duplicate Property named [x] found on class [bar.B]"
which in my opinion is coming from the abstract declaration in A, being inherited by B.
Setting the accessor-type for B to FIELD, gets rid of this error, unfortunately this is not an option because i do have an extra property in B to marshal which does not return a field but a calculated value, so i am stuck with PROPERTY (following works: setting accessor-type for B to FIELD and mapping the extra property with an #XmlPath annotation - but i dont want annotations in my code).
Being stuck with accessor-type PROPERTY for class B, my next attempt was:
<java-type name="foo.A" xml-accessor-type="NONE"/>
to prevent the abstract property from being inherited by B, which gets me:
Ignoring attribute [x] on class [bar.B] as no Property was generated for it.
Same is happening using this mapping:
<java-type name="foo.A" xml-accessor-type="PROPERTY">
<java-attributes>
<xml-transient java-attribute="x"/>
</java-attributes>
</java-type>
In both cases property 'x' is ignored.
I have really spent quite some time on this now - i cant imagine that its not possible to get this to work??
My workaround at the moment:
Leaving foo.A to be transient, specifying accessor-type FIELD for bar.B (which gets me property 'x' without problems) and mapping the extra property in B using an annotation in code.
But as mentioned before: I would like to solve this completely without annotations - anybody any idea? Blaise? :)
regards,
--qu
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
You appear to have hit a bug. You can track our progress on this issue at the following link. I have provided additional details on this issue below:
https://bugs.eclipse.org/367886
Using Annotations
If you were going to map this use case with JAXB/MOXy annotations you could set #XmlAccessorType(XmlAccessType.NONE) on the A class and do something like:
A
package forum8727402;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.NONE)
public abstract class A {
public abstract String getX();
}
B
package forum8727402;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
public class B extends A {
#XmlPath("a/b/c/text()")
private String x;
public B() {
x = "Hello World";
}
#Override
public String getX() {
return x;
}
#XmlElement
public String getCalculatedValue() {
return "Calculated Value";
}
}
Demo
package forum8727402;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(B.class);
B b = new B();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(b, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<b>
<a>
<b>
<c>Hello World</c>
</b>
</a>
<calculatedValue>Calculated Value</calculatedValue>
</b>
Using MOXy's External Mapping File
oxm.xml
Below is a MOXy external mapping file that represents the equivalent of the previously shown annotations:
<?xml version="1.0" encoding="UTF-8"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum8727402">
<java-types>
<java-type name="A" xml-accessor-type="NONE"/>
<java-type name="B">
<xml-root-element/>
<java-attributes>
<xml-element java-attribute="x" xml-path="a/b/c/text()"/>
<xml-element java-attribute="calculatedValue"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Demo
The code below demonstrates how to reference the mapping file:
package forum8727402;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum8727402/oxm.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {A.class, B.class}, properties);
B b = new B();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(b, System.out);
}
}
Output
[EL Warning]: 2012-01-04 14:45:46.366--Ignoring attribute [x] on class [forum8727402.xml.B] as no Property was generated for it.
<?xml version="1.0" encoding="UTF-8"?>
<b>
<calculatedValue>Calculated Value</calculatedValue>
</b>
I have a web server responding with xml data and a client consuming it.
Both share the same domain code. One of the domain objects looks like this:
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
#XmlRootElement(name = "image")
public class Image {
private String filename;
private ImageTypeEnum type;
#XmlElement(name = "imageUri")
public String getAbsoluteUri() {
// some complex computation
return uri;
}
}
When I try to unmarshal the response from the server into this object, since there's no setter for absoluteUri, I don't have the imageUri in the class. So I extend it like this:
public class FEImage extends Image{
private String imageUri;
public String getAbsoluteUri() {
return imageUri;
}
public void setAbsoluteUri(String imageUri) {
this.imageUri = imageUri;
}
}
My ObjectFactory
#XmlRegistry
public class ObjectFactory {
public Image createImage(){
return new FEImage();
}
}
My code to unmarshal is here:
JAXBContext context = JAXBContext.newInstance(ObjectFactory.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty("com.sun.xml.bind.ObjectFactory",new ObjectFactory());
((JAXBElement)unmarshaller.unmarshal((InputStream) response.getEntity())).getValue();
However, the setAbsoluteUri doesn't seem to be getting called in FEImage while unmarshalling. When I add a dummy setAbsoluteUri in Image.java, everything works as expected.
Can someone tell me how can I cleanly extend from Image.java?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
A JAXB implementation is not required to use the ObjectFactory class when instantiating an object. You can configure instantiation to be done via a factory class using the #XmlType annotation:
#XmlType(factoryClass=ObjectFactory.class, factoryMethod="createImage")
public class Image {
private String filename;
private ImageTypeEnum type;
#XmlElement(name = "imageUri")
public String getAbsoluteUri() {
// some complex computation
return uri;
}
}
http://blog.bdoughan.com/2011/06/jaxb-and-factory-methods.html
If you do the above, then your JAXB implementation will still use the Image class to derive the metadata so it will not solve your problem. An alternate approach would be to use an XmlAdapter for this use case:
http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.html
Better still, when a property on your domain object does not have a setter, you can tell your
JAXB implementation (EclipseLink MOXy, Metro, Apache JaxMe, etc) to use field (instance variable) access instead using #XmlAccessorType(XmlAccessType.FIELD):
#XmlAccessorType(XmlAccessType.FIELD)
public class Image {
}
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
UPDATE #1
If you are not able to modify the domain objects, then you may be interested in MOXy's externalized metadata. This extension provides a means via XML to provide JAXB metadata for classes where you cannot modify the source.
For More Information
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
http://wiki.eclipse.org/EclipseLink/UserGuide/MOXy/Runtime/XML_Bindings
UPDATE #2 - Based on results of chat
Image
Below is the implementation of the Image class that I will use for this example. For the complex computation of getAbsoluteUri() I simply add the prefix "CDN" to the filename:
package forum7552310;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
#XmlRootElement(name = "image")
public class Image {
private String filename;
private ImageTypeEnum type;
#XmlElement(name = "imageUri")
public String getAbsoluteUri() {
return "CDN" + filename;
}
}
binding.xml
Below is the MOXy binding document I put together. In this file I do a few things:
Set XmlAccessorType to FIELD
Mark the absoluteURI property to be XmlTransient since we will be mapping the filename field instead.
Specify that an XmlAdapter will be used with the filename field. This is to apply the logic that is done in the getAbsoluteUri() method.
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum7552310">
<java-types>
<java-type name="Image" xml-accessor-type="FIELD">
<java-attributes>
<xml-element java-attribute="filename" name="imageUri">
<xml-java-type-adapter value="forum7552310.FileNameAdapter"/>
</xml-element>
<xml-transient java-attribute="absoluteUri"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
FileNameAdapter
Below is the implementation of the XmlAdapter that applies the same name algorithm as the getAbsoluteUri() method:
package forum7552310;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class FileNameAdapter extends XmlAdapter<String, String> {
#Override
public String marshal(String string) throws Exception {
return "CDN" + string;
}
#Override
public String unmarshal(String adaptedString) throws Exception {
return adaptedString.substring(3);
}
}
Demo
Below is the demo code demonstrating how to apply the binding file when creating the JAXBContext:
package forum7552310;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum7552310/binding.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Image.class}, properties);
File xml = new File("src/forum7552310/input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
Image image = (Image) unmarshaller.unmarshal(xml);
System.out.println(image.getAbsoluteUri());
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(image, System.out);
}
}
jaxb.properties
You need to include a file named jaxb.properties with the following contents in the same package as your Image class:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
input.xml
Here is the XML input I used:
<?xml version="1.0" encoding="UTF-8"?>
<image>
<imageUri>CDNURI</imageUri>
</image>
Output
And here is the output from running the demo code:
CDNURI
<?xml version="1.0" encoding="UTF-8"?>
<image>
<imageUri>CDNURI</imageUri>
</image>