I will try to be as clear as I can. This question was inspired from a previously asked question from years ago.
I'd like to to encapsulate xml data into a single Root element object.
So for the following XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FosterHome>
<Orphanage>Happy Days Daycare</Orphanage>
<Location>Apple Street</Location>
<Families>
<Family>
<ParentID>Adams</ParentID>
<ChildList>
<ChildID>Child1</ChildID>
<ChildID>Child2</ChildID>
</ChildList>
</Family>
<Family>
<ParentID>Adams</ParentID>
<ChildList>
<ChildID>Child3</ChildID>
<ChildID>Child4</ChildID>
</ChildList>
</Family>
</Families>
<RemainingChildList>
<ChildID>Child5</ChildID>
<ChildID>Child6</ChildID>
</RemainingChildList>
</FosterHome>
I have the following JAXB annotated classes.
FosterHome.java
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="FosterHome")
#XmlAccessorType(XmlAccessType.FIELD)
public class FosterHome {
#XmlElement(name="Orphanage")
private String orphanage;
#XmlElement(name="Location")
private String location;
#XmlElementWrapper(name="Families")
#XmlElement(name="Family")
private List<Family> families;
#XmlElementWrapper(name="RemainingChildList")
#XmlElement(name="ChildID")
private List<String> remainingChildren;
}
Family.java
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Family {
#XmlElement(name="ParentID")
private String parentID;
#XmlElementWrapper(name="ChildList")
#XmlElement(name="ChildID")
private List<String> childList;
}
Demo.java
public static FosterHome parseXML(File file) {
try {
JAXBContext context = JAXBContext.newInstance(FosterHome.class);
Unmarshaller um = context.createUnmarshaller();
FosterHome fosterHome = (FosterHome) um.unmarshal(file);
for(Family family : fosterHome.getFamilies()) {
for(String childID : family.getChildList()) {
System.out.println(childID);
// my question is here
// How to encapsulate all the data into the fosterHome object
// such as:
// FosterHome fh = childID.getChildID();
}
}
return fosterHome;
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
FosterHome fh = parseXML(new File("./testdata/jaxbtest.xml"));
sysout(fh); // print out all the data from the xml example from above including the data for ChildID
}
So my question is:
How to get ChildID of the type FosterHome, it looks like ChildID is of the type Family, so if you wanted the fosterHome object to contain the childIDs, this may not work. Do anyone know of a way to accomplish this? Something like FosterHome.Family may not be possible since the classes are decoupled. So when I do a fosterHome.getChildID() I will get a childID of type FosterHome.
Related
I have a large XML file that consists of many events. I would like to unmarshal them. As it's a large file, I would like to unmarshal them one by one so the whole file is not stored in memory. It works for some events but fails for some due to the fact that it's unable to map to a particular class as it's already in the next event.
Note: I am aware of the XMLEventReader but most of them have mentioned it as not very memory efficient so I am trying to use XMLStreamReader and accomplish this.
Following is the sample XML file that contains the events:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<extension>
<extension>
<c>
<name>CName</name>
<age>CAge</age>
</c>
</extension>
</extension>
<extension>
<b>
<name>BName</name>
<age>BAge</age>
</b>
</extension>
<a>
<name>AName</name>
<age>AAge</age>
</a>
<extension>
<b>
<name>BName</name>
<age>BAge</age>
</b>
</extension>
I have 3 classes corresponding to them which will be used for unmarshalling:
#XmlRootElement(name = "a")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "a", propOrder = {"name","age"})
public class A
{
private String name;
private String age;
//Getter, Setter and other constructors
}
#XmlRootElement(name = "extension")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "extension", propOrder = {"name","age"})
public class B
{
#XmlPath("b/name/text()")
private String name;
#XmlPath("b/age/text()")
private String age;
//Getter, Setter and other constructors
}
#XmlRootElement(name = "extension")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "extension", propOrder = {"name","age"})
public class C
{
#XmlPath("extension/c/name/text()")
private String name;
#XmlPath("extension/c/age/text()")
private String age;
//Getter, Setter and other constructors
}
Following is my Main class which will be used for unmarshalling:
public class Main{
private Unmarshaller unmarshaller = null;
private JAXBContext jaxbContext = null;
public void unmarshaller(InputStream xmlStream) throws IOException, XMLStreamException, JAXBException {
final XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
final XMLStreamReader streamReader = xmlInputFactory.createXMLStreamReader(xmlStream);
//Navigate to next and start of the XML Elements
streamReader.next();
//Read Until the end of the file
while (streamReader.hasNext()) {
//Check if the element is "extension" if so its Class B or C
if (streamReader.isStartElement() && streamReader.getLocalName().equalsIgnoreCase("extension")) {
//Check if the next element also has "extension" if so its Class C
//This is IMPORTANT step for mapping b/w Class B & C which is confusing me
streamReader.next();
if (streamReader.isStartElement() && streamReader.getLocalName().equalsIgnoreCase("extension")) {
//If there is 2 extension tag then its Class C
classSpecifier(C.class);
final C cInfo = unmarshaller.unmarshal(streamReader, C.class).getValue();
System.out.println(cInfo);
}else{
//If there is no "extension" tag then its Class B
//THIS IS WHERE ITS FAILING: IF ITS NOT CLASS C THEN IT WOULD COME HERE BUT SINCE I HAVE
//ALREADY MOVED TO NEXT ELEMENT TO CHECK IF ITS "extension" ITS UNABLE TO MAP THE WHOLE CLASS TO CLASS B
classSpecifier(B.class);
final B bInfo = unmarshaller.unmarshal(streamReader, B.class).getValue();
System.out.println(bInfo);
}
}else if(streamReader.isStartElement() && streamReader.getLocalName().equalsIgnoreCase("a")){
//If there is no "extension" then its class A
classSpecifier(A.class);
final A aInfo = unmarshaller.unmarshal(streamReader, A.class).getValue();
System.out.println(aInfo);
}
}
}
//Method to initialize the JAXBContext and Unmarshaller based on the incoming eventType
private void classSpecifier(Class eventTypeClass) throws JAXBException {
this.jaxbContext = JAXBContext.newInstance(eventTypeClass);
unmarshaller = jaxbContext.createUnmarshaller();
}
public static void main(String args[]){
try{
InputStream xmlStream = Main.class.getClassLoader().getResourceAsStream("InputEPCISEvents.xml");
unmarshaller(xmlStream);
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}
}
}
The problem I am facing is the differentiating between class B and C.
I need to check if the incoming localName is extension.
If it's extension then I need to check if the next element localName is also extension.
If so then it's class C if not then class B.
Since in Step-2 I have already moved to streamreader.next() and if the element is not extension then its unable to map it to class B as I have already moved to next() element and it does not have the whole class.
I am looking for some solutions where I can do the following:
If the element in the 2nd verification is not extension then go back to the previous element then assign the whole class to class B.
Assign the streamReader to tempreader when making a check so that you will be advancing in tempreader. But this also failing.
Is there a way to go back to the previous element in a stream or else how can I tackle this issue? I hope I was able to provide a complete explanation.
"Going back" in a stream implies some kind of memory, so there is no point in sticking to the most memory-efficient tool.
XMLEventReader can handle this with ease:
public class Main {
public static void main(String args[]) throws Exception {
Unmarshaller aUnmarshaller = JAXBContext.newInstance(A.class).createUnmarshaller();
Unmarshaller bUnmarshaller = JAXBContext.newInstance(B.class).createUnmarshaller();
Unmarshaller cUnmarshaller = JAXBContext.newInstance(C.class).createUnmarshaller();
try (InputStream input = Main.class.getResourceAsStream("InputEPCISEvents.xml")) {
XMLEventReader reader = XMLInputFactory.newInstance().createXMLEventReader(input);
while (reader.hasNext()) {
XMLEvent event = reader.peek();
if (event.isStartElement()) {
switch (event.asStartElement().getName().getLocalPart()) {
case "a" -> System.out.println(aUnmarshaller.unmarshal(reader));
case "b" -> System.out.println(bUnmarshaller.unmarshal(reader));
case "c" -> System.out.println(cUnmarshaller.unmarshal(reader));
}
}
reader.next();
}
}
}
#XmlAccessorType(XmlAccessType.FIELD)
static class ABC {
String name;
String age;
public String toString() {
return getClass().getSimpleName() + "{name='" + name + "', age='" + age + "}";
}
}
#XmlRootElement static class A extends ABC {}
#XmlRootElement static class B extends ABC {}
#XmlRootElement static class C extends ABC {}
}
Output:
C{name='CName', age='CAge}
B{name='BName', age='BAge}
A{name='AName', age='AAge}
B{name='BName', age='BAge}
By the way, your XML needs to be wrapped in a parent element as it contains more than one root element.
I've got this POJO, encapsulating a dynamic, non-nested element of an Atom entry:
public class SimpleElement {
private Namespace namespace;
private String tagName;
private String value;
private Collection<Attribute> attributes;
/* getters/setters/... */
And for completeness, Attribute
public class Attribute {
private String name;
private String value;
private Namespace namespace;
/* getters/setters/... */
And Namespace:
public class Namespace {
private final String uri;
private final String prefix;
/* getters/setters/... */
SimpleElementAdapter serializes a SimpleElement into its org.w3c.dom.Element counterpart.
The only problem with this approach is that namespaces always end up at element level, never at document root.
Is there a way to dynamically declare namespaces at document root?
MY RECOMMENDATION
My recommendation is to let the JAXB implementation write the namespace declarations as it sees fit. As long as the elements are properly namespace qualified it does not really matter where the namespace declarations occur.
If you ignore my recommendation, below is an approach you can use.
ORIGINAL ANSWER
Specify the Namespaces to Include on Root Element
You can use the NamespacePrefixMapper extension to add extra namespace declarations to the root element (see: https://jaxb.java.net/nonav/2.2.11/docs/ch05.html#prefixmapper). You will need to derive from your own object model what namespaces should be declared at the root.
Note: NamespacePrefixMapper is in the com.sun.xml.bind.marshaller package. This means you will need the JAXB refereince implementation jar on your classpath (see: https://jaxb.java.net/).
import com.sun.xml.bind.marshaller.*;
public class MyNamespacePrefixMapper extends NamespacePrefixMapper {
#Override
public String getPreferredPrefix(String arg0, String arg1, boolean arg2) {
return null;
}
#Override
public String[] getPreDeclaredNamespaceUris2() {
return new String[] {"ns1", "http://www.example.com/FOO", "ns2", "http://www.example.com/BAR"};
}
}
Specify the NamespacePrefixMapper on the Marshaller
The com.sun.xml.bind.namespacePrefixMapper property is used to specify the NamespacePrefixMapper on the Marshaller.
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper());
Demo Code
Java Model (Foo)
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Foo {
private Object object;
#XmlAnyElement
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
}
Demo
import javax.xml.bind.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.w3c.dom.Element;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Foo foo = new Foo();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.newDocument();
Element element = document.createElementNS("http://www.example.com/FOO", "ns1:foo");
foo.setObject(element);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper());
marshaller.marshal(foo, System.out);
}
}
Output
Below is sample output that will be produced:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo xmlns:ns1="http://www.example.com/FOO" xmlns:ns2="http://www.example.com/BAR">
<ns1:foo/>
</foo>
UPDATE
Clear answer, thanks. However, I need access to the NSMapper from
SimpleElementAdapter. What do you suggest? The only way I see right
now is making the NSMapper a mutable singleton so that
SimpleElementAdapter can add namespaces if needed.
I forgot about your XmlAdapter.
Java Model
Below is a more complicated iteration of the model, where instead of Foo holding an instance of a DOM element, it holds and instance of Bar that gets adapted into an instance of a DOM element.
Foo
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
public class Foo {
private Bar bar;
#XmlAnyElement
#XmlJavaTypeAdapter(BarAdapter.class)
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
}
Bar
public class Bar {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
BarAdapter
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.*;
import org.w3c.dom.*;
public class BarAdapter extends XmlAdapter<Object, Bar>{
#Override
public Object marshal(Bar bar) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.newDocument();
Element element = document.createElementNS("http://www.example.com/BAR", "ns:bar");
element.setTextContent(bar.getValue());
return element;
}
#Override
public Bar unmarshal(Object arg0) throws Exception {
// TODO Auto-generated method stub
return null;
}
}
Grab Namespace Declarations
Since your object model does not hold the DOM elements directly you can't traverse it to get the namespace declarations. Instead we could do a marshal to a ContentHandler to collect them. Below are the reasons for marshalling to a ContentHandler:
It gives us an easy event which we can use to collection the namespace declarations.
It doesn't actually produce anything so it is the lightest marshal target we can use.
NsContentHandler contentHandler = new NsContentHandler();
marshaller.marshal(foo, contentHandler);
NsContentHandler
The implementation of ContentHandler will look something like:
import java.util.*;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class NsContentHandler extends DefaultHandler {
private Map<String, String> namespaces = new TreeMap<String, String>();
#Override
public void startPrefixMapping(String prefix, String uri) throws SAXException {
if(!namespaces.containsKey(prefix)) {
namespaces.put(prefix, uri);
}
}
public Map<String, String> getNamespaces() {
return namespaces;
}
}
Specify the Namespaces to Include on Root Element
The implementation of MyNamespacePrefixMapper changes a little to use the namrespaces captured from our ContentHandler.
import java.util.Map;
import java.util.Map.Entry;
import com.sun.xml.bind.marshaller.*;
public class MyNamespacePrefixMapper extends NamespacePrefixMapper {
private String[] namespaces;
public MyNamespacePrefixMapper(Map<String, String> namespaces) {
this.namespaces = new String[namespaces.size() * 2];
int index = 0;
for(Entry<String, String> entry : namespaces.entrySet()) {
this.namespaces[index++] = entry.getKey();
this.namespaces[index++] = entry.getValue();
}
}
#Override
public String getPreferredPrefix(String arg0, String arg1, boolean arg2) {
return null;
}
#Override
public String[] getPreDeclaredNamespaceUris2() {
return namespaces;
}
}
Demo Code
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Bar bar = new Bar();
bar.setValue("Hello World");
Foo foo = new Foo();
foo.setBar(bar);
Marshaller marshaller = jc.createMarshaller();
// Marshal First Time to Get Namespace Declarations
NsContentHandler contentHandler = new NsContentHandler();
marshaller.marshal(foo, contentHandler);
// Marshal Second Time for Real
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper(contentHandler.getNamespaces()));
marshaller.marshal(foo, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo xmlns:ns="http://www.example.com/BAR">
<ns:bar>Hello World</ns:bar>
</foo>
it this a bug?
I need nillable = "true" in my xsd schema. The only way to generate such an element from my java code is to use #XmlElement(nillable = true), right? But in this case, I will not be able to take advantage of this definition, I will not be able to check if the element is set to nil. The method isNil() is in the JAXBElement wrapper class.
So, what are my options here - I want to generate nillable = "true" in my xsd schema AND be able to check if it is set from my java code.
I need nillable = "true" in my xsd schema. The only way to generate
such an element from my java code is to use #XmlElement(nillable =
true), right?
Yes.
But in this case, I will not be able to take advantage of this
definition, I will not be able to check if the element is set to nil.
The method isNil() is in the JAXBElement wrapper class.
You can do getFoo() == null to determine if it was null. If you are trying to determine if the null corresponds to an absent element or xsi:nil="true" then you will have to do more. A set won't be done for absent nodes so you can put logic in your setter to track if a set to null was done because of an element with xsi:nil="true.
#XmlElement(nillable=true)
public String getFooString() {
return fooString;
}
public void setFooString(String foo) {
this.fooString = foo;
this.setFoo = true;
}
If you don't want to have this extra logic (which doesn't help for marshalling anyways you need to leverage JAXBElement.
#XmlElementRef(name="fooJAXBElement")
public JAXBElement<String> getFooJAXBElement() {
return fooJAXBElement;
}
public void setFooJAXBElement(JAXBElement<String> fooJAXBElement) {
this.fooJAXBElement = fooJAXBElement;
}
Java Model
Root
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlType(propOrder={"fooString", "barString", "fooJAXBElement", "barJAXBElement"})
public class Root {
private String fooString;
private String barString;
private JAXBElement<String> fooJAXBElement;
private JAXBElement<String> barJAXBElement;
private boolean setFoo;
private boolean setBar;
#XmlElement(nillable=true)
public String getFooString() {
return fooString;
}
public void setFooString(String foo) {
this.fooString = foo;
this.setFoo = true;
}
public boolean isSetFooString() {
return setFoo;
}
#XmlElement(nillable=true)
public String getBarString() {
return barString;
}
public void setBarString(String bar) {
this.barString = bar;
this.setBar = true;
}
public boolean isSetBarString() {
return setBar;
}
#XmlElementRef(name="fooJAXBElement")
public JAXBElement<String> getFooJAXBElement() {
return fooJAXBElement;
}
public void setFooJAXBElement(JAXBElement<String> fooJAXBElement) {
this.fooJAXBElement = fooJAXBElement;
}
#XmlElementRef(name="barJAXBElement")
public JAXBElement<String> getBarJAXBElement() {
return barJAXBElement;
}
public void setBarJAXBElement(JAXBElement<String> barJAXBElement) {
this.barJAXBElement = barJAXBElement;
}
}
ObjectFactory
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name="fooJAXBElement")
public JAXBElement<String> createFooJAXBElement(String string) {
return new JAXBElement<String>(new QName("fooJAXBElement"), String.class, string);
}
#XmlElementDecl(name="barJAXBElement")
public JAXBElement<String> createBarJAXBElement(String string) {
return new JAXBElement<String>(new QName("barJAXBElement"), String.class, string);
}
}
Demo
Below is a full example to demonstrate the concepts discussed above.
input.xml
This document contains 2 elements explicitly marked with xsi:nil="true" and 2 other mapped elements that are absent.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<barString xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<barJAXBElement xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</root>
Demo
This demo code will read in the above XML and check whether the properties have been set as a result of the unmarshal.
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class, ObjectFactory.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum20076018/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
System.out.println("Was fooString set? " + root.isSetFooString());
System.out.println("Was barString set? " + root.isSetBarString());
System.out.println("Was fooJAXBElement set? " + (root.getFooJAXBElement() != null));
System.out.println("Was barJAXBElement set? " + (root.getBarJAXBElement() != null));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
Below is the output from running the demo code. We see that all the is set checks are correct, but that the output only fully matches the input for the JAXBElement properties.
Was fooString set? false
Was barString set? true
Was fooJAXBElement set? false
Was barJAXBElement set? true
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<fooString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
<barString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
<barJAXBElement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</root>
In my XML I have
<myelem required="false"/>
How I can read the required attribute as a boolean? I can read it as String and inside a getter do this: return new Boolean(required)
But maybe there are some more elegant ways?
Just simply use boolean for the member in your Java class:
#XmlAttribute
private boolean required;
Or, if you use getter-setter style of mapping:
#XmlAttribute
public boolean isRequired() {
return required;
}
The JAXB unmarshaller is able to interpret "true" and "false" strings in the XML document as boolean value.
UPDATE:
I tested this with the following classes:
test/MyElem.java:
package test;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="myelem")
public class MyElem {
private boolean required;
#XmlAttribute
public boolean isRequired() {
return required;
}
public void setRequired(boolean value) {
required = value;
}
}
Test.java:
import javax.xml.bind.*;
import java.io.*;
import test.*;
public class Test {
public static void main(String[] args) {
try {
JAXBContext jc = JAXBContext.newInstance(MyElem.class);
Unmarshaller u = jc.createUnmarshaller();
Object o = u.unmarshal( new File( "test.xml" ) );
System.out.println(((MyElem)o).isRequired());
} catch(Exception e) {
e.printStackTrace();
}
}
}
And with the following input (test.xml):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myelem required="true"/>
I get the correct result on the console:
true
Is there any way to make JAXB not save fields which values are the default values specified in the #Element annotations, and then make set the value to it when loading elements from XML that are null or empties? An example:
class Example
{
#XmlElement(defaultValue="default1")
String prop1;
}
Example example = new Example();
example.setProp1("default1");
jaxbMarshaller.marshal(example, aFile);
Should generate:
<example/>
And when loading
Example example = (Example) jaxbUnMarshaller.unmarshal(aFile);
assertTrue(example.getProp1().equals("default1"));
I am trying to do this in order to generate a clean XML configuration file, and make it better readable and smaller size.
Regars and thanks in advance.
You could do something like the following by leveraging XmlAccessorType(XmlAccessType.FIELD) and putting logic in the get/set methods:
Example
package forum8885011;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
class Example {
private static final String PROP1_DEFAULT = "default1";
private static final String PROP2_DEFAULT = "123";
#XmlElement(defaultValue=PROP1_DEFAULT)
String prop1;
#XmlElement(defaultValue=PROP2_DEFAULT)
Integer prop2;
public String getProp1() {
if(null == prop1) {
return PROP1_DEFAULT;
}
return prop1;
}
public void setProp1(String value) {
if(PROP1_DEFAULT.equals(value)) {
prop1 = null;
} else {
prop1 = value;
}
}
public int getProp2() {
if(null == prop2) {
return Integer.valueOf(PROP2_DEFAULT);
}
return prop2;
}
public void setProp2(int value) {
if(PROP2_DEFAULT.equals(String.valueOf(value))) {
prop2 = null;
} else {
prop2 = value;
}
}
}
Demo
package forum8885011;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Example.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Example example = new Example();
example.setProp1("default1");
example.setProp2(123);
System.out.println(example.getProp1());
System.out.println(example.getProp2());
marshaller.marshal(example, System.out);
example.setProp1("FOO");
example.setProp2(456);
System.out.println(example.getProp1());
System.out.println(example.getProp2());
marshaller.marshal(example, System.out);
}
}
Output
default1
123
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<example/>
FOO
456
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<example>
<prop1>FOO</prop1>
<prop2>456</prop2>
</example>
For More Information
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
For a programmatic solution, there's also good old Apache commons XmlSchema and you can check against the default value with XmlSchemaElement.getDefaultValue()
So with something like
XmlSchemaElement elem = schema.getElementByName(ELEMENT_QNAME);
String defval = elem.getDefaultValue();
you should be able to do what you need. Haven't tried it out in the end, because I needed a more direct solution, but I hope that helps.