I have a java bean without any annotations. And I have a class inherited from this bean with JAXB annotations.
Jersey (JAX-RS) serialize the second class to JSON. And inherited properties occur in JSON twice: with name from XmlElement annotation and with 'camel-case' name of java-bean property. Here is a code which illustrates this:
class MyBean {
private Integer beanField;
public Integer getBeanField() { return beanField; }
public void setBeanField(Integer value) { this.beanField = value; }
}
#XmlRootElement
class AnnotatedBean extends MyBean {
#Override
#XmlElement(name="field")
public Integer getBeanField() { return super.getBeanField(); }
}
}
After serialization I get the next JSON:
{
"field" : 5,
"beanField" : 5
}
(while I want it to contain only one field with name field).
I investigated JAXB marshaller implementation and found that it marshals properties from all superclasses of the given class (and that means that it's impossible to get rid of the odd beanField property in my example).
But I still hope that I could miss something. Is there a way to serialize only annotated properties?
To get only the annotated properties use XmlAccessType.NONE:
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement
class AnnotatedBean extends MyBean {
...
}
Mapping the 3rd Party Class Using Externalized Metadata
You could use the external metadata extension in EclipseLink JAXB (MOXy), I'm the tech lead. It allows you to provide metadata for 3rd party classes. For this example the metadata will look like:
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="third.party.package">
<java-types>
<java-type name="MyBean" xml-transient="true"/>
</java-types>
</xml-bindings>
To use MOXy you need to add a file named jaxb.properties in with your model classes with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
The following article has instructions on configuring MOXy to work with Jersery:
http://bdoughan.blogspot.com/2010/08/creating-restful-web-service-part-35.html
Context Resolver - Leveraging the Metadata
You would need to use a ContextResolver to get your JAXBContext to leverage the external bindings file. The metadata is specified through a property when the JAXBContext is instantiated:
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
#Provider
#Produces({"application/xml", "application/json"})
public class AnnotatedBeanContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext jaxbContext;
public PurchaseOrderContextResolver() {
try {
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, new File("src/blog/bindingfile/binding.xml"));
jaxbContext = JAXBContext.newInstance(new Class[] {AnnotatedBean.class}, properties);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
public JAXBContext getContext(Class<?> clazz) {
if(AnnotatedBean.class == clazz) {
return jaxbContext;
}
return null;
}
}
Related
I'm using JAXB to marshall Java Objects into XML. The ide i'm using is JDeveloper 11.1.1.7.0 in windows OS. Whenever i run that particular java code i get an error message which says
"Error(1,1): file:/C:/JDeveloper/mywork/bugdashboard/Model/src/model/BugReport.xml<Line 1, Column 1>: XML-20108: (Fatal Error) Start of root element expected."
Even though there seem to be no errors in the code still i'm not able to get the desired output..Please Help..
My JAVA code..
package model;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
public class JavaObvjecttoXml {
public void xmlGenerator() {
//super();
JavaServiceFacade fcd = new JavaServiceFacade();
bugvalue track, track1, track2;
List<ReportDto> bugReport, bugrePort1, bugrePort2;
List<bugvalue> reportMetaData= new ArrayList<bugvalue>();
ReportMetaData rmd = new ReportMetaData();
try {
rmd.setBugreportmetadata(new ArrayList<bugvalue> ());
JAXBContext context = JAXBContext.newInstance(ReportMetaData.class);
Marshaller marshaller;
marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
track = new bugvalue();
bugReport = fcd.getBugSeverityReport();
track.setBugReport(bugReport);
track.setLabel("Bug by severity");
track.setTile("severity");
track.setChatType("PIE");
track.setXAxisLabel("Label");
track.setYAxisLAbel("Label Count");
track1 = new bugvalue();
bugrePort1 = fcd.getBugStatusReport();
track1.setBugReport(bugrePort1);
track1.setLabel("Bug by Status");
track1.setTile("status");
track1.setChatType("bar");
track1.setXAxisLabel("count");
track1.setYAxisLAbel("Label");
track2 = new bugvalue();
bugrePort2 = fcd.getBugCategoryReport();
track2.setBugReport(bugrePort2);
track2.setLabel("Bug by Category");
track2.setTile("category");
track2.setChatType("PIE");
track2.setXAxisLabel("count");
track2.setYAxisLAbel("Label");
reportMetaData.add(track);
reportMetaData.add(track1);
reportMetaData.add(track2);
rmd.setBugreportmetadata(reportMetaData);
File output = new File("C:\\JDeveloper\\mywork\\bugdashboard\\Model\\src\\model\\BugReport.xml");
marshaller.marshal(rmd, output);
}
catch(JAXBException e){
e.printStackTrace();
}
}
/**
* #param args
*/
public static void main(String[] args) {
JavaObvjecttoXml obj = new JavaObvjecttoXml();
obj.xmlGenerator();
}
}
File ReportMetaData.java
package model;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
# XmlRootElement(name = "ReportMetaData")
public class ReportMetaData {
private List<bugvalue> bugreportmetadata = new ArrayList<bugvalue>();
public List<bugvalue> getBugreportmetadata() {
return bugreportmetadata;
}
*/ #param bugreportmetadata
*/
public void setBugreportmetadata(List<bugvalue> bugreportmetadata) {
this.bugreportmetadata = bugreportmetadata;
}
public ReportMetaData() {
super();
}
}
The error message..
The file BugReport.xml
As you can see from the above screen shot the file BugReport.xml is being generated but it's empty..
I suspect you need to annotate the fields you intend to output when you mashall the object to XML. Java™ sources generated by the JAX-B schema compiler I use apply the following annotation to the object attribute definitions:
#XmlElement(required = true)
In this case, the relevant section of your ReportMetaData class would look like this:
#XmlElement(required = true)
private List<bugvalue> bugreportmetadata = new ArrayList<bugvalue>();
The JAX-B schema compiler also generates two additional annotations before the #XmlRootElement annotation:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"bugreportmetadata"
})
In a JAX-B generated class structure, each class definition has these annotations. In addition, the #XmlType annotations for the nested class definitions contain the name of the type, so that the annotation for the bugvalue class might look like this:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "bugvalue", propOrder = {
// TODO: list the fields for BugValue as defined
})
The all lower case definition of bugvalue, depending on the coding for the JAX-B marshaller, may also create a problem. When marshalling an object, the JAX-B processor has to dynamically access and class definitions and generate element identifiers from them. If the JAX-B processor relies on Java naming conventions to do this, code that does not adhere to these conventions may potentially fail.
I have a class which contains an ArrayList(SuperClass) property. Now I wish to unmarshall the following XML file which contains different element names in that collection because these are subclasses of the Superclass. Is there a way of doing this with Moxy?
<?xml version="1.0" encoding="UTF-8"?>
<SmMessageSet xmlns:nav="urn:ccsds:recommendation:navigation:schema:ndmxml:R1.5"
xmlns="urn:ccsds:recommendation:service_management:schema:sccs:R1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:ccsds:recommendation:service_management:schema:sccs:R1.0 file:/C:/CCSDS-910.11-B-1_XML_schemas/CCSDS-910.11-B-1_XML_schemas/SmSchema-v1.0.0.xsd">
<sccsSmVersionRef>sccsSmVersionRef0</sccsSmVersionRef>
<smSource>smSource0</smSource>
<smDestination>smDestination0</smDestination>
<serviceAgreementRef>serviceAgreementRef0</serviceAgreementRef>
<smMessages>
<querySpaceCommunicationServiceProfileFailedReturn>
<messageSequenceNumber>50</messageSequenceNumber>
<messageTimestamp>2006-05-04T18:13:51.0</messageTimestamp>
<invocationMessageSequenceNumber>50</invocationMessageSequenceNumber>
<spaceCommunicationServiceProfileRef>spaceCommunicationServiceProfileRef0
</spaceCommunicationServiceProfileRef>
<qscspError>
<erroredItem>erroredItem0</erroredItem>
<diagnostic>operation timeout</diagnostic>
</qscspError>
<qscspError>
<erroredItem>erroredItem1</erroredItem>
<diagnostic>operation timeout</diagnostic>
</qscspError>
</querySpaceCommunicationServiceProfileFailedReturn>
<createUserAccountInvocation1>
<messageSequenceNumber>50</messageSequenceNumber>
<messageTimestamp>2006-05-04T18:13:51.0</messageTimestamp>
<username>createdUser</username>
<password>createdPassword</password>
<firstname>Test</firstname>
<lastname>User</lastname>
<email>test.user#host.de</email>
<role>SCHEDULING_OFFICER</role>
<superuser>0</superuser>
</createUserAccountInvocation1>
</smMessages>
</SmMessageSet>
The querySpaceCommunicationServiceProfileFailedReturn and createUserAccountInvocation are in my java object model subclasses of SmMessage base class, which is held by the SmMessageSet class in an, as above described, ArrayList of SmMessage classes.
I would also like to not change the current XML structure (i.e. create a wrapper element around the SmMessages in the XML file).
Any help would be appreciated :)
You could do the following leveraging #XmlElementWrapper and #XmlElementRef:
Java Model
SmMessageSet
You can use the #XmlElementWrapper annotation to add a grouping element around the collection (see: http://blog.bdoughan.com/2010/09/jaxb-collection-properties.html). You can also use the #XmlElementRef annotation to model the element name as the inheritance indicator (substitution groups in XML Schema, see: http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html).
package forum20745762;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="SmMessageSet")
#XmlAccessorType(XmlAccessType.FIELD)
public class SmMessageSet {
#XmlElementWrapper
#XmlElementRef
private List<SmMessage> smMessages;
}
SmMessage
JAXB/MOXy won't automatically pull in all subclasses of a class, so you can use the #XmlSeeAlso annotation to have them pulled in.
package forum20745762;
import javax.xml.bind.annotation.XmlSeeAlso;
#XmlSeeAlso({CreateUserAccountInvocation.class, QuerySpaceCommunicationServiceProfileFailedReturn1.class})
public class SmMessage {
}
CreateUserAccountInvocation
One each of the subclasses you need to annotate with #XmlRootElement. This is the element name that the #XmlElementRef annotation will match on.
package forum20745762;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class CreateUserAccountInvocation extends SmMessage {
}
QuerySpaceCommunicationServiceProfileFailedReturn1
package forum20745762;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class QuerySpaceCommunicationServiceProfileFailedReturn1 extends SmMessage {
}
package-info
We will use the package level #XmlSchema annotation to map the namespaces (see: http://blog.bdoughan.com/2010/08/jaxb-namespaces.html).
#XmlSchema(
namespace="urn:ccsds:recommendation:service_management:schema:sccs:R1.0",
elementFormDefault=XmlNsForm.QUALIFIED
)
package forum20745762;
import javax.xml.bind.annotation.*;
Demo Code
Demo
The following demo code will read the XML from your question, and then write it back out.
package forum20745762;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(SmMessageSet.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum20745762/input.xml");
SmMessageSet smMessageSet = (SmMessageSet) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(smMessageSet, System.out);
}
}
Output
The output below corresponds to just the subset of your XML document that I had mapped to:
<?xml version="1.0" encoding="UTF-8"?>
<SmMessageSet xmlns="urn:ccsds:recommendation:service_management:schema:sccs:R1.0">
<smMessages>
<querySpaceCommunicationServiceProfileFailedReturn1/>
<createUserAccountInvocation/>
</smMessages>
</SmMessageSet>
I have a requirement, to marshall/unmarshall some elements of java pojo depending upon a custom annotation marked on the field.
suppose there are 3 fields in my java pojp
#CustomVersion("v1")
private String field1;
#CustomVersion("v1","v2")
private String field2;
#CustomVersion("v2")
private String field3;
i would like to marshall only the fields with v1 if i pass version="v1" parameter while conversion in jaxb. if i pass v2, all fields with v2 annotation should only be marshalled.
is that even possible using jaxb? i am sure selective marshalling would be supported through some library or way, am not still able to figure it out after quite some searching.
any help or advice or pointers are highly appreciated.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Below is an example of how you could use MOXy's #XmlNamedObjectGraphs extension to map your use case.
Java Model
Foo
The #XmlNamedObjectGraphs extension allows you to specify multiple subsets of mappings identified by a key.
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlNamedAttributeNode;
import org.eclipse.persistence.oxm.annotations.XmlNamedObjectGraph;
import org.eclipse.persistence.oxm.annotations.XmlNamedObjectGraphs;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlNamedObjectGraphs({
#XmlNamedObjectGraph(
name="v1",
attributeNodes = {
#XmlNamedAttributeNode("field1"),
#XmlNamedAttributeNode("field2")}),
#XmlNamedObjectGraph(
name="v2",
attributeNodes = {
#XmlNamedAttributeNode("field2"),
#XmlNamedAttributeNode("field3")})
})
public class Foo {
private String field1 = "ONE";
private String field2 = "TWO";
private String field3 = "THREE";
}
jaxb.properties
To use MOXy as your JAXB provider you need to include a file called jaxb.properties with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo Code
Demo
You can specify the key corresponding to the object graph to have that subset applied to the object you are marshalling.
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import org.eclipse.persistence.jaxb.MarshallerProperties;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Foo foo = new Foo();
// Marshal Everything
marshaller.marshal(foo, System.out);
// Marshal "v1" Data
marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, "v1");
marshaller.marshal(foo, System.out);
// Marshal "v2" Data
marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, "v2");
marshaller.marshal(foo, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<field1>ONE</field1>
<field2>TWO</field2>
<field3>THREE</field3>
</foo>
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<field1>ONE</field1>
<field2>TWO</field2>
</foo>
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<field2>TWO</field2>
<field3>THREE</field3>
</foo>
For More Information
http://blog.bdoughan.com/2013/03/moxys-object-graphs-inputoutput-partial.html
First of all I would suggest doing such preprocessing before marshalling. It would be much easier. However if it is not possible for some reason then you can create you custom type adapter. Then you can put #XmlJavaTypeAdapter(VersioningAdapter.class) on every type that you want to have versioning enabled.
#XmlJavaTypeAdapter can also be specified on package level, but you have to specify to which types it applies. You cannot use XmlAdapter without specifying somewhere #XmlJavaTypeAdapter.
Drawbacks of such solution:
if you have multiple versioned types then each of them has to be annotated with #XmlJavaTypeAdapter
#XmlJavaTypeAdapter does not work for root element, only on child elements. You have to call adapter manually on root element before marshalling
AFAIK there is no other option for customizing JAXB marshalling. That's why I think that annotation processing should be performed in separate step before marshalling. Unless you can accept mentioned limitations.
Sample adapter (full code can be found here):
public class VersioningAdapter extends XmlAdapter<Object, Object> {
#Override
public Object unmarshal(Object v) throws Exception {
// TODO Auto-generated method stub
return null;
}
#Override
public Object marshal(Object v) throws Exception {
if (v == null) {
return v;
}
Field[] fields = v.getClass().getDeclaredFields();
for (Field field : fields) {
Annotation[] annotations = field.getDeclaredAnnotations();
CustomVersion annotation = findCustomVersion(annotations);
if (annotation != null) {
if (!contains(annotation, Configuration.getVersion())) {
field.setAccessible(true);
field.set(v, null);
}
}
}
return v;
}
private CustomVersion findCustomVersion(Annotation[] annotations) {
for (Annotation annotation : annotations) {
if (annotation instanceof CustomVersion) {
return (CustomVersion) annotation;
}
}
return null;
}
private boolean contains(CustomVersion annotation, String version) {
String[] values = annotation.value();
for (String value : values) {
if (value.equals(version)) {
return true;
}
}
return false;
}
}
I have written a JAX-RS (Jersey) REST Service, which accepts XML messages of ONIX XML format. Generally, I have generated all the required classes for JAXB binding from the given schema with xjc. There are more than 500 classes overall and I cannot modify them.
Now, when I have a JAXB-mapped object, I need to store it to the database. I work with mongoDb, so the message format should be JSON. I tried to use Jackson with JAXB module to convert JAXB-object into JSON, which works pretty fine with storing the data. But when I try to convert the JSON back into the JAXB object, it throws an exception connected somehow with the JAXBElement. In google I found out that the JAXBElement is not supported in Jackson and I have to work around this issue. But I cant do it because I cannot modify JAXB-generated classes.
Is there a way to map JAXB Objects into JSON with some other means, but which will follow the whole JAXB specification so that I have no problems in the future converting from JSON to the JAXB object and visa vera?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
If you can't do it with Jackson, you use case will work with MOXy.
Java Model
Foo
Here is a sample class that contains a field of type JAXBElement.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
#XmlElementRef(name="bar")
private JAXBElement<Bar> bar;
}
Bar
public class Bar {
}
ObjectFactory
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name = "bar")
public JAXBElement<Bar> createBar(Bar bar) {
return new JAXBElement<Bar>(new QName("bar"), Bar.class, bar);
}
}
Standalone Demo Code
Below is some demo code you can run in Java SE to see that everything works:
Demo
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
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>(2);
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
JAXBContext jc = JAXBContext.newInstance(new Class[] {Foo.class, ObjectFactory.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource json = new StreamSource("src/forum19158056/input.json");
Foo foo = unmarshaller.unmarshal(json, Foo.class).getValue();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
}
}
input.json/Output
{"bar" : {} }
Running with JAX-RS
The following links will help you leverage MOXy in a JAX-RS service:
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html
http://blog.bdoughan.com/2013/06/moxy-is-new-default-json-binding.html
Since you're using Jackson you can construct an ObjectMapper with the JaxbAnnotationModule and write out the value. The following is some code to write a JAXB annotated object to system out.
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JaxbAnnotationModule());
mapper.writeValue(System.out, jaxbObject);
This approach will also work on Glassfish as it does not utilize the provider given by the container which would cause ClassNotFoundException as per GLASSFISH-21141
I would like to add a processing instruction whenever a collection/array property is serialized to get something like
<alice>
<? array bob ?>
<bob>edgar</bob>
<bob>david</bob>
</alice>
Is this possible with JAXB? Or at least with some specific JAXB implementation?
You could leverage an XMLStreamWriter and an XmlAdapter to do this:
BobAdapter
Things to note about the XmlAdapter:
It's stateful and references an XMLStreamWriter. We will later leverage JAXB's ability to set a stateful XmlAdapter on a Marshaller.
It converts from a List<String> to a List<String>, we're just using an XmlAdapter here to get an event.
The marshal method is where we call writeProcessingInstruction on the XMLStreamWriter:
package forum6931520;
import java.util.List;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.stream.XMLStreamWriter;
public class BobAdapter extends XmlAdapter<List<String>, List<String>> {
private boolean first = true;
private XMLStreamWriter xmlStreamWriter;
public BobAdapter() {
}
public BobAdapter(XMLStreamWriter xmlStreamWriter) {
this();
this.xmlStreamWriter = xmlStreamWriter;
}
#Override
public List<String> marshal(List<String> stringList) throws Exception {
if(first) {
xmlStreamWriter.writeProcessingInstruction("array", "bob");
first = false;
}
return stringList;
}
#Override
public List<String> unmarshal(List<String> stringList) throws Exception {
return stringList;
}
}
Alice
The #XmlJavaTypeAdapter annotation is used to link the XmlAdapter with the field/property:
package forum6931520;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
public class Alice {
private List<String> bob;
#XmlJavaTypeAdapter(BobAdapter.class)
public List<String> getBob() {
return bob;
}
public void setBob(List<String> bob) {
this.bob = bob;
}
}
Demo
package forum6931520;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Alice.class);
File xml = new File("src/forum6931520/input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
Alice alice = (Alice) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
XMLOutputFactory xof = XMLOutputFactory.newFactory();
XMLStreamWriter xmlStreamWriter = xof.createXMLStreamWriter(System.out);
marshaller.setAdapter(new BobAdapter(xmlStreamWriter));
marshaller.marshal(alice, xmlStreamWriter);
}
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<alice>
<?array bob?>
<bob>edgar</bob>
<bob>david</bob>
</alice>
Output
<?xml version='1.0' encoding='UTF-8'?><alice><?array bob?><bob>edgar</bob><bob>david</bob></alice>
Note
This example needs to be run with EclipseLink JAXB (MOXy) as the JAXB RI will throw the following exception (I'm the MOXy lead):
Exception in thread "main" com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
java.util.List is an interface, and JAXB can't handle interfaces.
this problem is related to the following location:
at java.util.List
at public java.util.List forum6931520.Alice.getBob()
at forum6931520.Alice
java.util.List does not have a no-arg default constructor.
this problem is related to the following location:
at java.util.List
at public java.util.List forum6931520.Alice.getBob()
at forum6931520.Alice
For More Information
http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
UPDATE
I have entered an enhancement request (https://bugs.eclipse.org/354286) to add native support for processing instructions. This would eventually allow you to specify the following on your property:
#XmlProcessingInstruction(target="array", value="bob")
public List<String> getBob() {
return bob;
}