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>
Related
I want to unmarshal xml content like this:
JAXBContext context = JAXBContext.newInstance(Node.class);
String input = "<item><d>foo</d></item>";
StreamSource istream = new StreamSource(new StringReader(input));
JAXBElement<Node> node = context.createUnmarshaller().unmarshal(istream, Node.class);
System.out.println(node.getValue().toString());
The d element cannot be trivially converted into the desired object, here a Boolean, so I use a setter method for that.
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "item")
public class Node {
#XmlElement(name = "d")
private Boolean value;
public void setValue(String d) {
this.value = d.equals("") ? Boolean.TRUE : Boolean.FALSE;
}
#Override
public String toString() {
return value.toString();
}
}
The XmlAnnotaion can be at setValue as well however when I add a getter method it has to be at the field(otherwise there is no parse and value remains null).
public Boolean getValue() {return value;}
But if instead of foo the value starts with a digit or a special character, say ?, there is again no parse.
Summary of effects:
annotation at field + no getter -> parses foo
annotation at setter + no getter -> parses foo and 0foo
annotation at field + getter -> parses foo
annotation at setter + getter -> parses neither
How do I need do set the annotations so that I can parse arbitrary values in d and why does it work here for strings that start with a letter?
Because XML is a typed language, and the types of the getter and setter need to match.
If. this is like, a real thing you're getting paid to do then you need to go and spend some time reading the jaxb documentation and probably the xml schema documentation.
here's some code because why not.
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "item")
public class Node {
private Boolean value;
#XmlElement(name = "d", nillable = true, required = false)
public void setValue(String d) {
this.value = "".equals(d) ? Boolean.TRUE : Boolean.FALSE;
}
public String getValue(String d) {
return String.valueOf(value);
}
public boolean isValue() {
return Boolean.TRUE.equals(value);
}
#Override
public String toString() {
return "[Node|value=" + value + "]";
}
public static void main(String... args) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Node.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Node node = (Node) jaxbUnmarshaller.unmarshal(new StringReader("<item><b></b></item>"));
System.out.println(node);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
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.
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.
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")?