JAXB Avoid saving default values - java

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.

Related

JAXB Multiline Attribute

IS there a way to marshall/unmarshall xml/object multiline attributes .
Currently it is showing as
<task name="Payament Gateway
Checker" >
It would be best , if store it by replacing nextline whitespace with \n ?
<task name="Payament Gateway \n Checker" >
Edit -
Tried Both Adapter and JaxbListener - but converted to name="Payament Gateway
Checker"
void beforeMarshal(Marshaller marshaller) {
name = name.replaceAll("\n", "
");
}
#XmlJavaTypeAdapter(MultilineString.class)
#XmlAttribute
protected String name;
public class MultilineString extends XmlAdapter<String, String> {
#Override
public String marshal(String input) throws Exception {
return input.replaceAll("\n", "
");
}
#Override
public String unmarshal(String output) throws Exception {
return output;
}
}
TL;DR
JAXB should be replacing the Java \n with
which is the proper XML escaping. If you are not seeing this then there may be a bug in the version of the JAXB implementation you are using. For example this issue was fixed in EclipseLink JAXB (MOXy) in version 2.5.1:
http://bugs.eclipse.org/414608
Example
Java Model (Foo)
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Foo {
private String bar;
#XmlAttribute
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
Demo Code
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Foo foo = new Foo();
foo.setBar("Hello\nWorld");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo bar="Hello
World"/>
Workaround
You can always use an XmlAdapter to control any output in JAXB.

JAXB generate nillable = "true" from java

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>

Is it possible to provide JAXB with a mapping information regarding element values?

I'm using JAXB to generate a xml file from my java objects (xml export), as well as the other way arround (xml import).
In some cases I'm using a "magic-number" to initialize a integer class attribute, because 0 is also valid an I want to initialize the attribute and mark it as "not-yet-edited".
In the xml output generated from JAXB I would be happy if this magic-number is not existing. Is it possible to provide JAXB with something like a mapping information?
Please have a look at the example.
Example:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="my-root")
public class ExampleClass {
/** integer value which represents empty */
public static final int EMPTY_INT = Integer.MAX_VALUE;
/** my id */
#XmlElement(name="id")
private int mMyId = EMPTY_INT;
public void setMyId(int myId) {
mMyId = myId;
}
public int getMyId() {
return mMyId;
}
}
JAXB generates someting like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<my-root>
<id>2147483647</id>
</my-root>
What I want is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<my-root>
<id></id>
</my-root>
I need to tell JAXB to generate "nothing" (see example) if the attribute value is EMPTY_INT and the other way arround (import).
Is that possible somehow?
Or are there other ways to reach that goal?
Thank you for your help.
Steffen
UPDATE:
Based on the answers I tried the following:
Note: The code is shorted (e. g. without imports)
1) add a class: Mydapter
public class MyAdapter extends XmlAdapter<String, Integer> {
#Override
public Integer unmarshal(String val) throws Exception {
System.out.println("Debug1");
return Integer.parseInt(val);
}
#Override
public String marshal(Integer val) throws Exception {
System.out.println("Debug2");
if (val == Integer.MAX_VALUE) {
return "";
} else {
return val.toString();
}
}
}
2) adapted ExampleClass to use "Integer" instead of "int" and annotade it
#XmlJavaTypeAdapter(MyAdapter.class)
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="my-root")
public class ExampleClass {
/** integer value which represents empty */
public static final int EMPTY_INT = Integer.MAX_VALUE;
/** my id */
#XmlElement(name="id")
private Integer mMyId = EMPTY_INT;
public void setMyId(int myId) {
mMyId = myId;
}
public int getMyId() {
return mMyId;
}
}
3) Code performing the xml export
public class XMLImportExport {
public static void exportToXml(File xmlFile) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ExampleClass.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(new ExampleClass(), xmlFile);
}
}
4) xml output is still
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<my-root>
<id>2147483647</id>
</my-root>
Thank you,
Steffen
Annotate mMyId with #XmlJavaTypeAdapter(YourAdapter.class) and then write an adapter to do the job. Something like this (untested) :
public class YourAdapter extends XmlAdapter<String, Integer> {
public Integer unmarshal(String val) throws Exception {
return Integer.parseInt(val);
}
public String marshal(Integer val) throws Exception {
if ( val == Integer.MAX_VALUE) {
return "";
} else {
return val.toString();
}
}
}

JAXB. Get boolean from string

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

Why doesn't JAXB handle namespaced child elements properly?

I'm using JAXB with a CXF Web Service. When I pass:
<?xml version="1.0" ?><ns2:Optionen xmlns:ns2="http://test.at/ezustellung/global/20090501#">
<ns2:PdfAKonvertierung>true</ns2:PdfAKonvertierung><ns2:SignaturTyp>Adobe</ns2:SignaturTyp>
</ns2:Optionen>
to the Unmarshaller, the properties pdfAKonvertierung und signaturTyp are both null in my Object. But if I pass:
<?xml version="1.0" ?><ns2:Optionen xmlns:ns2="http://test.at/ezustellung/global/20090501#">
<PdfAKonvertierung>true</PdfAKonvertierung><SignaturTyp>Adobe</SignaturTyp>
</ns2:Optionen>
which is invalid, according to the CXF Validator and wikipedia (translation, haven't found that in english):
Child elements of an element with a namespace prefix do not automatically have the same namespace, they have to be prefixed with a namespace as well.
The properties are set accordingly. Can someone spot an error in my code, or have I found a bug in the JAXB implementation from Java 1.6?
For reference, here is my code:
public class JaxbTests {
#Test
public void testOptionen() throws JAXBException, SAXException, IOException {
JAXBContext context = JAXBContext.newInstance(Optionen.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
ByteArrayInputStream is = new ByteArrayInputStream(
// Does not work
("<?xml version=\"1.0\" ?><ns2:Optionen xmlns:ns2=\"http://test.at/ezustellung/global/20090501#\">" +
"<ns2:PdfAKonvertierung>true</ns2:PdfAKonvertierung><ns2:SignaturTyp>Adobe</ns2:SignaturTyp>" +
"</ns2:Optionen>").getBytes());
// Works
// ("<?xml version=\"1.0\" ?><ns2:Optionen xmlns:ns2=\"http://test.at/ezustellung/global/20090501#\">" +
// "<PdfAKonvertierung>true</PdfAKonvertierung><SignaturTyp>Adobe</SignaturTyp>" +
// "</ns2:Optionen>").getBytes());
Optionen opts = ((Optionen) unmarshaller.unmarshal(is));
Assert.assertTrue(opts.isPdfAKonvertierung() == true);
Assert.assertEquals(SignaturTypType.ADOBE, opts.getSignaturTyp());
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"pdfAKonvertierung",
"signaturTyp"
})
#XmlRootElement(name = "Optionen")
public class Optionen {
#XmlElement(name = "PdfAKonvertierung", defaultValue = "true")
protected Boolean pdfAKonvertierung;
#XmlElement(name = "SignaturTyp", defaultValue = "Adobe")
protected SignaturTypType signaturTyp;
public Optionen() {
System.out.println("Optionen created");
}
public Boolean isPdfAKonvertierung() {
return pdfAKonvertierung;
}
public void setPdfAKonvertierung(Boolean value) {
this.pdfAKonvertierung = value;
}
public SignaturTypType getSignaturTyp() {
return signaturTyp;
}
public void setSignaturTyp(SignaturTypType value) {
this.signaturTyp = value;
}
}
#XmlType(name = "SignaturTypType")
#XmlEnum
public enum SignaturTypType {
#XmlEnumValue("Adobe")
ADOBE("Adobe"), #XmlEnumValue("PDF-AS")
PDF_AS("PDF-AS");
private final String value;
SignaturTypType(String v) {
this.value = v;
}
public String value() {
return this.value;
}
public static SignaturTypType fromValue(String v) {
for (SignaturTypType c : SignaturTypType.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
package-info.java:
#javax.xml.bind.annotation.XmlSchema(namespace = "http://test.at/ezustellung/global/20090501#")
package at.test.ezustellung.global._20090501_;
The solution (without modifying the generated classes) to the problem was to make sure that the
<xs:schema elementFormDefault="qualified">
attribute is present and to regenerate the jaxb mapping, so
that
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED
ends up in the package-info.java
It's a little bit difficult to read your incorrectly formatted code, but wouldn't it fix your problem if you declare your elements with the correct namespace, e.g. #XmlElement(namespace = "http://test.at/ezustellung/global/20090501#", name = "SignaturTyp", defaultValue = "Adobe")?

Categories

Resources