How to unmarshal empty element into empty string with JAXB - jaxb

There is a pseudo code like this:
Alma alma = new Alma();
alma.setKorte(""); //Korte is a string member
marshaller.marshal(alma, stringwriter);
System.out.println(stringwriter.toString());
And it produces the output of (I know this is some kind of trick that the empty element is there, but this is how it works in my system, so someone before me have set this like this):
<alma><korte/></alma>
Which is fine for me. But when I unmarshal it, the empty string is not unmarshalled correctly, but korte will be null. How to make jaxb to unmarshal the empty element into empty string?
I use JDK6 bundled jaxb.
EDIT:
The alma class looks like (name of class is changed, but it is like this):
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Alma", propOrder = {
"korte"
})
public class Alma
implements Serializable
{
private final static long serialVersionUID = 100L;
#XmlElement(required = true)
protected String korte;

JAXB implementations should unmarshal empty elements as "" for String properties. The solution will be to upgrade to a newer version of your JAXB implementation that contains this fix.
The example below worked for me using the version of JAXB included in JDK 1.6.0_20 and EcliseLink JAXB (MOXy) 2.3.
Demo
import java.io.StringReader;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Alma.class);
String xmlString = "<alma><korte/></alma>";
StringReader xmlReader = new StringReader(xmlString);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Alma alma = (Alma) unmarshaller.unmarshal(xmlReader);
System.out.println(alma.getKorte().length());
}
}
Output
0
Alma
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
#XmlType(name = "Alma", propOrder = { "korte" })
public class Alma implements Serializable {
private final static long serialVersionUID = 100L;
#XmlElement(required = true)
protected String korte;
public String getKorte() {
return korte;
}
public void setKorte(String korte) {
this.korte = korte;
}
}

Related

Create xml string for java jaxb bean with Root class & inner classes having different name spaces

I have a Root Class called Employee, which has two elements empid and name and another jaxb class called Address. Below is the sample snippet.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Request",propOrder = {
"header",
"body",
"signature"
})
#XmlRootElement(name="Employee")
public class Employee
implements Serializable
{
#XmlElement(name = "Header", required = true)
protected String empId;
#XmlElement(name = "Body", required = true)
protected String empName;
#XmlElement(name = "Address", required = true)
protected Address address;
.. setters and getters
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Address", propOrder = {
"streetLine1",
"streetLine2",
})
#XmlRootElement(name="Address",namespace= "http://www.w3.org/2000/09/xmldsig#")
public class Employee
implements Serializable
{
private final static long serialVersionUID = 100L;
#XmlElement(name = "addressLine1", required = true)
protected String addressLine1;
#XmlElement(name = "addressLine2", required = true)
protected String addressLine2;
//Setters and getters
}
Now when I generate the XML string with jaxb marshalling I want the expected result like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Employee xmlns="http://www.test.com">
<empId>124</empId>
<empName>name</empName>
<Address xmlns:ns2="http://www.w3.org/2000/09/xmldsig#">
<ns2:streetLine1 Id="line1"/>
<ns2:streetLine2 Id="Line2"/>
</Address>
</Request>
Please suggest. Thanks in Advance.
There are some problems with your JAXB classes, I think you have copy pasted and changed some names wrongly. The following elements inside #XMLType must be defined as #XMLElement.
#XmlType(name = "Request",propOrder = {
"header",
"body",
"signature"
})
Anyways assuming the classes are right. You will need 2 changes to generate XML that has elements referred in different namespace.
Move the namespace to package level using #XMLSchema. i.e Add package-info.java at the package level to specify the namespaces.
Provide Address element with its own namespace in Employee class. Each element if not in parent namespace, must be overridden at this level.
package-info.java
#XmlSchema(
namespace = "http://www.test.com",
elementFormDefault = XmlNsForm.QUALIFIED)
package int1.d;
import javax.xml.bind.annotation.*;
Employee.java
package int1.d;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Request",propOrder = {
"header",
"body",
"signature"
})
#XmlRootElement(name="Employee")
public class Employee
implements Serializable
{
private static final long serialVersionUID = 8293193254658211943L;
#XmlElement(name = "Header", required = true)
protected String empId;
#XmlElement(name = "Body", required = true)
protected String empName;
#XmlElement(name = "Address", namespace="http://www.w3.org/2000/09/xmldsig#", required = true )
protected Address address;
public String getEmpId() {
return empId;
}
public void setEmpId(String empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
package-info.java
#XmlSchema(
namespace = "http://www.w3.org/2000/09/xmldsig#",
elementFormDefault=XmlNsForm.QUALIFIED )
package int1.d2;
import javax.xml.bind.annotation.*;
Address.java
package int1.d2;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Address", propOrder = {
"streetLine1",
"streetLine2",
})
#XmlRootElement(name="Address")
public class Address
implements Serializable
{
private final static long serialVersionUID = 100L;
#XmlElement(name = "addressLine1", required = true)
protected String addressLine1;
#XmlElement(name = "addressLine2", required = true)
protected String addressLine2;
public String getAddressLine1() {
return addressLine1;
}
public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}
public String getAddressLine2() {
return addressLine2;
}
public void setAddressLine2(String addressLine2) {
this.addressLine2 = addressLine2;
}
}
output generated by JAXB
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Employee xmlns="http://www.test.com" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#">
<Header>124</Header>
<Body>bae</Body>
<ns2:Address>
<ns2:addressLine1>line1</ns2:addressLine1>
<ns2:addressLine2>line2</ns2:addressLine2>
</ns2:Address>
</Employee>

How to unmarshall JSON array using EclipseLink MOXy JAXB provider

This seems simple, but I must be missing something. All I am trying to do is unmarshall a JSON array. I have code that works when the JSON is a simple object, but when I make it an array (ie..surround JSON with []), it fails. Here is the sample json, domain class, unmarshalling code, and exception. Any help will be greatly appreciated.
Sample JSON:
[{"CreateIndex":24988,"ModifyIndex":132476,"LockIndex":0,"Key":"Redirector","Flags":0}
]
Domain:
package eclipselink.example.moxy.json.simple.model;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.base.Optional;
import com.google.common.io.BaseEncoding;
import com.orbitz.consul.model.kv.ImmutableValue;
import com.orbitz.consul.util.UnsignedLongDeserializer;
#org.immutables.value.Value.Immutable
#JsonDeserialize(as = ImmutableValue.class)
#JsonSerialize(as = ImmutableValue.class)
#JsonIgnoreProperties(ignoreUnknown = true)
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class MyValue {
#XmlAttribute
private long createIndex;
#XmlAttribute
private long modifyIndex;
#XmlAttribute
private long lockIndex;
#XmlAttribute
private String key;
#XmlElement
private long flags;
#XmlElement(nillable = true)
private Optional<String> value;
#XmlElement
private Optional<String> session;
#JsonProperty("CreateIndex")
public long getCreateIndex() {
return createIndex;
}
#JsonProperty("ModifyIndex")
public long getModifyIndex() {
return modifyIndex;
}
#JsonProperty("LockIndex")
public long getLockIndex() {
return lockIndex;
}
#JsonProperty("Key")
public String getKey() {
return key;
}
#JsonProperty("Flags")
#JsonDeserialize(using=UnsignedLongDeserializer.class)
public long getFlags() {
return flags;
}
#JsonProperty("Value")
public Optional<String> getValue() {
return value;
}
#JsonProperty("Session")
public Optional<String> getSession() {
return session;
}
#JsonIgnore
#org.immutables.value.Value.Lazy
public Optional<String> getValueAsString() {
if (getValue() != null && getValue().isPresent()) {
return Optional.of(
new String(BaseEncoding.base64().decode(getValue().get()))
);
} else {
return Optional.absent();
}
}
}
Unmarshall code:
public class Main_JSON_redirector {
private static final String INPUT_XML = "META-INF/input.xml";
private static final String INPUT_JSON_URL = "http://192.168.85.186:8500/v1/kv/Redirector";
private static final File INPUT_JSON_FILE = new File("C:/Users/dnance/EclipseLink-examples/moxy/json-simple/src/main/resources/META-INF/redirector.json");
public static void main(String[] args) throws Exception {
System.out.println();
System.out.println("Running EclipseLink MOXy Simple MAIN_JSON Example");
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.JSON_WRAPPER_AS_ARRAY_NAME, true);
JAXBContext jc = JAXBContext.newInstance(new Class[] {MyValue.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);
unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);
StreamSource source = new StreamSource(INPUT_JSON_FILE);
JAXBElement<MyValue[]> jaxbElement = unmarshaller.unmarshal(source, MyValue[].class);
MyValue[] value = jaxbElement.getValue();
System.out.println("value: " + value[0].getValueAsString());
...
Exception:
Running EclipseLink MOXy Simple MAIN_JSON Example
Exception in thread "main" javax.xml.bind.UnmarshalException
- with linked exception:
[Exception [EclipseLink-25007] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: A descriptor for class [Leclipselink.example.moxy.json.simple.model.MyValue; was not found in the project. For JAXB, if the JAXBContext was bootstrapped using TypeMappingInfo[] you must call a marshal method that accepts TypeMappingInfo as an input parameter.]
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.handleXMLMarshalException(JAXBUnmarshaller.java:1072)
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:341)
at eclipselink.example.moxy.json.simple.Main_JSON_redirector.main(Main_JSON_redirector.java:58)
Caused by: Exception [EclipseLink-25007] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: A descriptor for class [Leclipselink.example.moxy.json.simple.model.MyValue; was not found in the project. For JAXB, if the JAXBContext was bootstrapped using TypeMappingInfo[] you must call a marshal method that accepts TypeMappingInfo as an input parameter.
at org.eclipse.persistence.exceptions.XMLMarshalException.descriptorNotFoundInProject(XMLMarshalException.java:154)
at org.eclipse.persistence.internal.oxm.Context$ContextState.getSession(Context.java:137)
at org.eclipse.persistence.oxm.XMLContext$XMLContextState.getSession(XMLContext.java:798)
at org.eclipse.persistence.oxm.XMLContext$XMLContextState.getSession(XMLContext.java:1)
at org.eclipse.persistence.internal.oxm.Context.getSession(Context.java:458)
at org.eclipse.persistence.oxm.XMLContext.getSession(XMLContext.java:366)
at org.eclipse.persistence.oxm.XMLContext.getSession(XMLContext.java:1)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:837)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:710)
at org.eclipse.persistence.internal.oxm.XMLUnmarshaller.unmarshal(XMLUnmarshaller.java:643)
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:339)
You just need to specify type class instead of array class in unmarshal method, E.g.
Collection<MyValue> myValues = (Collection<MyValue>) unmarshaller.unmarshal(source, MyValue.class).getValue();
Since you have unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false); it just works. Without it unmarshal returns JAXBElement<Collection<JAXBElement>>.

Line number of individual XML element while unmarshalling using jaxb

I have a class Person with attributes name and address. I display it in a XML. While unmarshalling from XML will it be possible to get line number for name and address separately.
I tried using Locator. But it does not provide individual line numbers.
The EclipseLink JAXB (MOXy) and the JAXB reference implementation each have their own #XmlLocation annotations for supporting this use case. This allows you to store the location on the XML element corresponding to the object as an instance of org.xml.sax.Locator. Since I'm the MOXy lead, I will demonstrate using MOXy:
Person
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.eclipse.persistence.oxm.annotations.XmlLocation;
import org.xml.sax.Locator;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Person {
#XmlJavaTypeAdapter(value=StringAdapter.class)
String name;
Address address;
#XmlLocation
Locator locator;
}
Address
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.eclipse.persistence.oxm.annotations.XmlLocation;
import org.xml.sax.Locator;
public class Address {
#XmlJavaTypeAdapter(value=StringAdapter.class)
private String street;
#XmlLocation
Locator locator;
}
StringAdapter
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.eclipse.persistence.oxm.annotations.XmlLocation;
import org.xml.sax.Locator;
public class StringAdapter extends XmlAdapter<StringAdapter.AdaptedString, String> {
public static class AdaptedString {
#XmlValue
public String value;
#XmlLocation
#XmlTransient
Locator locator;
}
#Override
public String unmarshal(AdaptedString v) throws Exception {
System.out.println(v.value + " " + v.locator.getLineNumber());
return v.value;
}
#Override
public AdaptedString marshal(String v) throws Exception {
AdaptedString adaptedString = new AdaptedString();
adaptedString.value = v;
return adaptedString;
}
}
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry.
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Person.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum14455596/input.xml");
Person person = (Person) unmarshaller.unmarshal(xml);
System.out.println("Person: " + person.locator.getLineNumber());
System.out.println("Address: " + person.address.locator.getLineNumber());
}
}
Output
Jane Doe 3
1 A Street 5
Person: 2
Address: 4
You could leverage a StAX StreamReaderDelegate and do something like the following:
Demo
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.stream.util.StreamReaderDelegate;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Person.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource source = new StreamSource("src/forum14455596/input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(source);
xsr = new StreamReaderDelegate(xsr) {
#Override
public String getLocalName() {
String localName = super.getLocalName();
if(isStartElement()) {
System.out.println(localName + " " + this.getLocation().getLineNumber());
}
return localName;
}
};
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.unmarshal(xsr);
}
}
Person
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Person {
private String name;
private String address;
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<person>
<name>Jane Doe</name>
<address>1 A Street</address>
</person>
Output
person 2
name 3
address 4

jaxb suppress outer tag for nested objects

I have a complex object I'm getting back as a return value from the usual "API I have no control over".
For some API calls the returned XML looks like:
<APICall1>
<VeryComplexObject>
<VeryComplexObjectElements... >
</VeryComplexObject>
</APICall1>
No problem, I just use
#XmlElement
private VeryComplexObject VeryComplexObject;
and it's business as usual.
But a few calls want to return:
<APICall2>
<VeryComplexObjectElements... >
</APICall2>
Is there an annotation I can use to suppress the <VeryComplexObject> tags for unmarshal but get the inner element tags?
You could use JAXB with StAX to accomplish this by leveraging a StreamFilter to ignore an XML element:
package forum8526002;
import java.io.StringReader;
import javax.xml.bind.*;
import javax.xml.bind.annotation.*;
import javax.xml.stream.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
StringReader xml = new StringReader("<APICall2><VeryComplexObjectElements><Bar>Hello World</Bar></VeryComplexObjectElements></APICall2>");
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
xsr = xif.createFilteredReader(xsr, new Filter());
Unmarshaller unmarshaller = jc.createUnmarshaller();
Foo foo = (Foo) unmarshaller.unmarshal(xsr);
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(foo, System.out);
}
#XmlRootElement(name="APICall2")
static class Foo {
#XmlElement(name="Bar")
private String bar;
}
static class Filter implements StreamFilter {
#Override
public boolean accept(XMLStreamReader reader) {
return !(reader.isStartElement() && reader.getLocalName().equals("VeryComplexObjectElements"));
}
}
}

JAXB: unmarshal an XML file with a given XML schema

I have a XML file, and its xml schema contains couples complexType in it. So when I unmarshal the xml file, I want to give the xml parser my xml schema. Is it possible to do it, if so, then how to do it?
EDIT: After I unmarshal, every field in my object is null. Any idea why?
UPDATE
The issue you are seeing is due to the content being nested within the NameAndAddress element. You could introduce a NameAndAddress class have PackageLabel hold an instance of that.
PackageLabel
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="PackageLabel")
#XmlAccessorType(XmlAccessType.FIELD)
public class PackageLabel implements Serializable {
#XmlElement(name="NameAndAddress")
private NameAndAddress nameAndAddress;
}
NameAndAddress
import javax.xml.bind.annotation.XmlElement;
public class NameAndAddress {
#XmlElement(name="Name")
private String name;
#XmlElement(name="Address1")
private String address1;
#XmlElement(name="Address2")
private String address2;
#XmlElement(name="City")
private String city;
#XmlElement(name="State")
private String state;
#XmlElement(name="ZipCode")
private String zipCode;
}
EclipseLink JAXB (MOXy)'s #XmlPath Extension
Alternatively you could use the #XmlPath extension in EclipseLink JAXB (MOXy):
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="PackageLabel")
#XmlAccessorType(XmlAccessType.FIELD)
public class PackageLabel implements Serializable {
#XmlPath("NameAndAddress/Name/text()")
private String name;
#XmlPath("NameAndAddress/Address1/text()")
private String address1;
#XmlPath("NameAndAddress/Address2/text()")
private String address2;
#XmlPath("NameAndAddress/City/text()")
private String city;
#XmlPath("NameAndAddress/State/text()")
private String state;
#XmlPath("NameAndAddress/ZipCode/text()")
private String zipCode;
}
For More Information
http://bdoughan.blogspot.com/2010/09/xpath-based-mapping-geocode-example.html
http://bdoughan.blogspot.com/2010/07/xpath-based-mapping.html
You can set an XML schema on an instance of Unmarshaller. This will cause JAXB to validate the input while it converts the XML to objects:
http://bdoughan.blogspot.com/2010/12/jaxb-and-marshalunmarshal-schema.html
If you want to generate an object model from an XML schema, you can also use JAXB to do that:
http://bdoughan.blogspot.com/2010/09/processing-atom-feeds-with-jaxb.html

Resources