How I can retrieve null value, when unmarshalling, if inside XML attribute value is empty ? Now I make inside my getters checking for null :
public String getLabel() {
if (label.isEmpty()) {
return null;
}
else {
return label;
}
}
But may be exist some other, more elegant way?
Thanks.
I think your XML looks more or less like this:
<myElement></myElement>
This, unfortunately, means, that you are passing an empty string.
If you want to pass null you have two options:
Do not pass this tag at all (your XML should not contain <myElement/> tag at all).
Use xsi:nil.
If using xsi:nil, first you have to declare your xml element (in XSD file) as nilable, like this:
<xsd:element name="myElement" nillable="true"/>
Then, to pass the null value inside XML do this:
<myElement xsi:nil="true"/>
or this:
<myElement xsi:nil="true"></myElement>
This way, JAXB knows, that you are passing null instead of an empty String.
The answer given by npe is a good one, and specifying how you want null represented would be my recommendation as well. To have xsi:nil marshalled you will want to annotate your property as (see Binding to JSON & XML - Handling Null):
#XmlElement(nillable=true)
public String getLabel() {
return label;
}
If you don't want to change your XML representation then you could use an XmlAdapter:
EmptyStringAdapter
package forum10869748;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class EmptyStringAdapter extends XmlAdapter<String, String> {
#Override
public String unmarshal(String v) throws Exception {
if("".equals(v)) {
return null;
}
return v;
}
#Override
public String marshal(String v) throws Exception {
return v;
}
}
Foo
You reference an XmlAdapter through the use of the #XmlJavaTypeAdapter annotation. If you would like this XmlAdapter applied to all Strings then you could register it at the package level (see JAXB and Package Level XmlAdapters).
package forum10869748;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
public class Foo {
private String label;
#XmlJavaTypeAdapter(EmptyStringAdapter.class)
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
}
Demo
package forum10869748;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum10869748/input.xml");
Foo foo = (Foo) unmarshaller.unmarshal(xml);
System.out.println(foo.getLabel());
}
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<label></label>
</foo>
Output
null
Related
I have XML like the following:
<repository>
<location>/home/username whitespace/somedir</location>
</repository>
I am using JAXB to unmarshal this into a JAXB annotated Bean.
The "location" XML element is mapped to java.net.URI class.
The problem is that when location contains white space, JAXB (or perhaps the underlying XML parser) can't handle this and location is set to null. There is no UnmarshalException or anything, just setLocation(URI loc) is called with a null method argument.
A URI without a white space works correctly of course.
The thing is that I can't really change RepositoryDTO to say have String location; field.
Can you tell what are my options here?
Should I look at URLEncode and then Decode the said location field?
This a REST/Jersey use case by the way, although clearly the culprit lies in JAXB/XML parser...
public class Whitespace {
public static void main(String[] args) throws JAXBException {
String xml = "<repository><location>/home/username whitespace/somedir</location></repository>";
Unmarshaller createUnmarshaller = JAXBContext.newInstance(RepositoryDTO.class).createUnmarshaller();
RepositoryDTO repositoryInfo = (RepositoryDTO) createUnmarshaller.unmarshal(new StringReader(xml));
System.out.println(repositoryInfo.getLocation());
}
#XmlRootElement(name = "repository")
static public class RepositoryDTO {
private URI location;
#XmlElement
public URI getLocation() {
return location;
}
public void setLocation(URI loc) {
this.location = loc;
}
}
}
You can use this adapter
import java.net.URI;
import java.net.URISyntaxException;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class UriAdapter extends XmlAdapter<String, URI> {
public URI unmarshal(String s) {
if(s != null){
try {
return new URI(s.replace(" ", "%20"));
} catch (URISyntaxException e) {
}
}
return null;
}
public String marshal(URI uri) {
return uri.getPath();
}
}
In this way
#XmlJavaTypeAdapter(UriAdapter.class)
protected URI location;
Try using the standart URI encoding for whitespace (replace space inside the XML element with %20)
Simple question! I have such a class, and if the attribute is empty (or just null) then this attribute should not appear at all in the generated XML file
XML File: (note empty string in attribute abc, i don't want something like that!)
<root abc="">
<example>somethin</example>
</root>
Java Class
#Root
public class Data {
#Element(name="example">
private String value;
#Attribute(name="abc", required=false)
private String s;
public String getString() {
return s;
}
I tried with a #Convert but it works only with #Element... Is there any way to remove the attribute in the xml file when it is empty?
Use a custom XMLAdapter as such:
public class ExampleAdapter extends XmlAdapter<String, String> {
#Override
public String marshal(String exampleString) throws Exception {
return exampleString;
}
#Override
public String unmarshal(String exampleString) throws Exception {
if (exampleString.isEmpty()) {
return null;
}
return exampleString;
}}
And then just associate your property with it:
#XmlAttribute
#XmlJavaTypeAdapter(ExampleAdapter.class)
public void setExample(String example) {
this.example = example;
}
I have a class, that is something like this:
public class Property {
private double floorArea;
public double getFloorArea() {
return floorArea;
}
#XmlElement
public void setFloorArea(double floorArea) {
this.floorArea = floorArea;
}
}
Which will give me something like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<property>
<floorArea>x</floorArea>
</property>
But I need something like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<property>
<floorArea>
<value>x</value>
</floorArea>
</property>
The API I am using requires it this way. My limited JAXB knowledge is preventing me from figuring this out. Any help is appreciated.
EDIT:
something I am researching. Would I need to create a value class with its own JAXB annotations for this to work? (and set floorArea to the type of value)?
Below is how your use case could be supported with an XmlAdapter using any JAXB (JSR-222) implementation.
XmlAdapter (DoubleValueAdapter)
An XmlAdapter is a mechanism that allows an object to be converted to another type of object . Then it is the converted object that is converted to/from XML.
package forum14045961;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class DoubleValueAdapter extends XmlAdapter<DoubleValueAdapter.AdaptedDoubleValue, Double>{
public static class AdaptedDoubleValue {
public double value;
}
#Override
public AdaptedDoubleValue marshal(Double value) throws Exception {
AdaptedDoubleValue adaptedDoubleValue = new AdaptedDoubleValue();
adaptedDoubleValue.value = value;
return adaptedDoubleValue;
}
#Override
public Double unmarshal(AdaptedDoubleValue adaptedDoubleValue) throws Exception {
return adaptedDoubleValue.value;
}
}
Property
The #XmlJavaTypeAdapter annotation is used to specify the XmlAdapter. I needed to change double to Double so I moved the mapping to the field as to not affect the public API.
package forum14045961;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Property {
#XmlJavaTypeAdapter(DoubleValueAdapter.class)
private Double floorArea;
public double getFloorArea() {
return floorArea;
}
public void setFloorArea(double floorArea) {
this.floorArea = floorArea;
}
}
Demo
Below is some demo code to prove that everything works.
package forum14045961;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Property.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum14045961/input.xml");
Property property = (Property) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(property, System.out);
}
}
input.xml/Output
Below is the input to and output from the demo code.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<property>
<floorArea>
<value>1.23</value>
</floorArea>
</property>
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Below is how you could map your use case using MOXy's #XmlPath extension:
Property
package forum14045961;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
public class Property {
private double floorArea;
public double getFloorArea() {
return floorArea;
}
#XmlPath("floorArea/value/text()")
public void setFloorArea(double floorArea) {
this.floorArea = floorArea;
}
}
jaxb.properties
To specify MOXy as your JAXB (JSR-222) provider you need to include a file called jaxb.properties in the same package as your domain model 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
Only the standard JAXB runtime APIs are required to read the objects from and write them back to XML when MOXy is used as the JAXB provider.
package forum14045961;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Property.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum14045961/input.xml");
Property property = (Property) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(property, System.out);
}
}
input.xml/Output
Below is the input to and output from running the demo code.
<?xml version="1.0" encoding="UTF-8"?>
<property>
<floorArea>
<value>1.23</value>
</floorArea>
</property>
For More Information
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
Your hunch is correct the way described will generate the xml as you have displayed.
public class Property {
#XmlElement(required = true)
protected FloorArea floorArea;
public FloorArea getFloorArea() {
return floorArea;
}
public void setFloorArea(FloorArea value) {
this.floorArea = value;
}
}
And your FloorArea class would look something like the code snapshot below.
public class FloorArea {
#XmlElement(required = true)
protected String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
The goal is to produce the following XML with JAXB
<foo>
<bar>string data</bar>
<bar>binary data</bar>
</foo>
Is there a workaround to allow generic #XmlValue fields (I need to store byte[] and String data)? Below is what I desire:
#XmlRootElement
public class Foo {
private #XmlElement List<Bar> bars;
}
#XmlRootElement
public class Bar<T> {
private #XmlValue T value; // (*)
}
But I get this exception
(*) IllegalAnnotationException:
#XmlAttribute/#XmlValue need to reference a Java type that maps to text in XML.
You could leverage an XmlAdapter for this use case instead of #XmlValue:
BarAdapter
package forum8807296;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class BarAdapter extends XmlAdapter<Object, Bar<?>> {
#Override
public Bar<?> unmarshal(Object v) throws Exception {
if(null == v) {
return null;
}
Bar<Object> bar = new Bar<Object>();
bar.setValue(v);
return bar;
}
#Override
public Object marshal(Bar<?> v) throws Exception {
if(null == v) {
return null;
}
return v.getValue();
}
}
Foo
The XmlAdapter is associated with the bars property using the #XmlJavaTypeAdapter annotation:
package forum8807296;
import java.util.List;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
public class Foo {
private List<Bar> bars;
#XmlElement(name="bar")
#XmlJavaTypeAdapter(BarAdapter.class)
public List<Bar> getBars() {
return bars;
}
public void setBars(List<Bar> bars) {
this.bars = bars;
}
}
Bar
package forum8807296;
public class Bar<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
Demo
You can test this example using the following demo code:
package forum8807296;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Foo foo = new Foo();
List<Bar> bars = new ArrayList<Bar>();
foo.setBars(bars);
Bar<String> stringBar = new Bar<String>();
stringBar.setValue("string data");
bars.add(stringBar);
Bar<byte[]> binaryBar = new Bar<byte[]>();
binaryBar.setValue("binary data".getBytes());
bars.add(binaryBar);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
}
}
Output
Note how the output includes the xsi:type attributes to preserve the type of the value. You can eliminate the the xsi:type attribute by having your XmlAdapter return String instead of Object, if you do this you will need handle the conversion from String to the appropriate type yourself for the unmarshal operation:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo>
<bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">string data</bars>
<bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:base64Binary">YmluYXJ5IGRhdGE=</bars>
</foo>
I couldn't get #XmlValue working as I always got NullPointerException along the way—not sure why. I came up with something like the following instead.
Drop your Bar class entirely, because, as you want it to be able to contain anything you can simply represent it with Object.
#XmlRootElement(name = "foo", namespace = "http://test.com")
#XmlType(name = "Foo", namespace = "http://test.com")
public class Foo {
#XmlElement(name = "bar")
public List<Object> bars = new ArrayList<>();
public Foo() {}
}
Without telling JAXB which namespaces your types are using every bar element inside a foo would contain separate namespace declarations and stuff—the package-info.java and all the namespace stuff serves only fancification purposes only.
#XmlSchema(attributeFormDefault = XmlNsForm.QUALIFIED,
elementFormDefault = XmlNsForm.QUALIFIED,
namespace = "http://test.com",
xmlns = {
#XmlNs(namespaceURI = "http://test.com", prefix = ""),
#XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi"),
#XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema", prefix = "xs")})
package test;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
Running this simple test would spout-out something similar to your XML snippet.
public static void main(String[] args) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(Foo.class);
Foo foo = new Foo();
foo.bars.add("a");
foo.bars.add("b".getBytes());
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(foo, System.out);
}
Output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://test.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<bar xsi:type="xs:string">a</bar>
<bar xsi:type="xs:base64Binary">Yg==</bar>
</foo>
Is there a reason you don't simply construct a String with your byte[]? Do you truly need a generic?
The trick I'm usually using is to create schema with types you want and then use xjc to generate Java classes and see how annotations are used. :) I believe in XML schema proper type mapping for byte[] is 'base64Binary', so creating schema like this:
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/NewXMLSchema" xmlns:tns="http://www.example.org/NewXMLSchema" elementFormDefault="qualified">
<element name="aTest" type="base64Binary"></element>
</schema>
and running xjc we would get following code generated:
#XmlElementDecl(namespace = "http://www.example.org/NewXMLSchema", name = "aTest")
public JAXBElement<byte[]> createATest(byte[] value) {
return new JAXBElement<byte[]>(_ATest_QNAME, byte[].class, null, ((byte[]) value));
}
I am refactoring some code to use JAXB and reflection to output code to the client, it is currently using an XMLWriter and manually creating the tags each time.
The problem I am having is that due to constraints on the client side, I need to have empty elements in the XML for any null fields in the java class.
While I realize this problem can be solved by adding nillable=true to each JAXB XmlElement annotation, that is not the most practical, as I have a lot of those annotations.
I was hoping to find a way to set nillable=true as a global attribute (or as the default value). This would also make it easier for future colleagues to work on it, as they won't need to remember that every annotation should include the nillable attribute.
I haven't found much besides descriptions of the default behavior. I find it surprising that no one else has posted a similar question in the past. From what I have found, it doesn't seem to me that there is any built-in support for making the default configurable. Is this something that might be solved with a custom JAXB implementation or maybe a third party JAXB implementation?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
I have entered an enhancement request to have this behaviour added to EclipseLink JAXB (MOXy):
http://bugs.eclipse.org/368547
WORK AROUND
As a work around if all your mapped String fields/properties are mapped to XML elements then the following XmlAdapter approach may work for you:
NullStringAdapter
This XmlAdapter will marshal instances of String as an object called AdaptedString. AdaptedString contains the String value as well as a field mapped to the xsi:nil attribute. In the XmlAdapter we will set the value of that field based on whether or not the String value is null.
package forum8841221;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;
public class NullStringAdapter extends XmlAdapter<NullStringAdapter.AdaptedString, String> {
#Override
public AdaptedString marshal(String v) throws Exception {
AdaptedString adaptedString = new AdaptedString();
if(null == v) {
adaptedString.nil = true;
}
adaptedString.value = v;
return adaptedString;
}
#Override
public String unmarshal(AdaptedString v) throws Exception {
return v.value;
}
public static class AdaptedString {
#XmlAttribute(namespace="http://www.w3.org/2001/XMLSchema-instance")
public Boolean nil;
#XmlValue
#XmlJavaTypeAdapter(VoidStringAdapter.class)
public String value;
}
public static class VoidStringAdapter extends XmlAdapter<String, String> {
#Override
public String marshal(String v) throws Exception {
return v;
}
#Override
public String unmarshal(String v) throws Exception {
return v;
}
}
}
package-info
We can register that we want this XmlAdapter to apply to all the mapped String fields/properties on this package by registering the XmlAdapter at the package level.
#XmlJavaTypeAdapter(value=NullStringAdapter.class, type=String.class)
#XmlSchema(xmlns={#XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi")})
package forum8841221;
import javax.xml.bind.annotation.adapters.*;
import javax.xml.bind.annotation.*;
Root
Below is the domain class I have used for this example. It has several String properties, one of them is annotated with #XmlElement(nillable=true)
package forum8841221;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Root {
private String a;
private String b;
private String c;
private String d;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
#XmlElement(nillable=true)
public String getD() {
return d;
}
public void setD(String d) {
this.d = d;
}
}
Demo
package forum8841221;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
root.setB("B");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<a xsi:nil="true"/>
<b>B</b>
<c xsi:nil="true"/>
<d xsi:nil="true"/>
</root>