JAX WS, JAXB and Null elements with attributes - attributes

I am trying to consume a web service using JAXWS and wsimport. The WSIMPORT tool generated all the required classes and I can invoke the service without any issues.
However, I noticed in cases where response contains a nil element with valid attribute values, JAXWS fails to unmarshall it and throws a NullPointerException. I used SOAP UI to help debug and here's what I found. The response returns the following XML (Excerpt):
<externalIdentifiers>
<identifierType code="2" name="Passport" xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<identifierValue/>
<issuingCountry xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</externalIdentifiers>
In my Java code, when trying to read the "name" property of identifier type as above, it throws a NPE:
if(id.getIdentifierType() == null)
{
System.out.println("NULL");
}
System.out.println("Identifier Type: " + id.getIdentifierType().getName());
Output:
NULL
Exception in thread "main" java.lang.NullPointerException
To me that does looks a reasonable response as in the response, identifierType is set as xsi:nil="true". That is also perfectly valid XML as per W3C. Question is, how do I read the attribute values such as code and name in such a case?

Below is how you can support this use case:
Java Model
ExternalIdentifiers
You can change the identifierType property to be of type JAXBElement<IdentifierType> instead of IdentifierType. To do this you will need to annotate the property with #XmlElementRef.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ExternalIdentifiers {
#XmlElementRef(name="identifierType")
private JAXBElement<IdentifierType> identifierType;
public JAXBElement<IdentifierType> getIdentifierType() {
return identifierType;
}
}
ObjectFactory
You will need a corresponding #XmlElementDecl annotation on a create method in a class annotated with #XmlRegistry.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name="identifierType")
public JAXBElement<IdentifierType> createIdentifierType(IdentifierType identifierType) {
return new JAXBElement(new QName("identifierType"), IdentifierType.class, identifierType);
}
}
Demo Code
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<externalIdentifiers>
<identifierType code="2" name="Passport" xsi:nil="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
</externalIdentifiers>
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ExternalIdentifiers.class, ObjectFactory.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum18834036/input.xml");
ExternalIdentifiers externalIdentifiers = (ExternalIdentifiers) unmarshaller.unmarshal(xml);
System.out.println(externalIdentifiers.getIdentifierType().getValue().getName());
}
}
Output
Passport
Note
Currently there is a bug in EclipseLink JAXB (MOXy) regarding this use case:
http://bugs.eclipse.org/404944

Related

unmarshalling into single class using eclipselink

i have created a Company class that does produce xml like below using marshalling :
<?xml version="1.0" encoding="UTF-8"?>
<ns2:company xmlns:ns2="http://www.example.com/">
<ns2:employee>
<job>sogi</job>
<name>togi</name>
<age>22</age>
</ns2:employee>
</ns2:company>
Note:I used #XmlPath("employee/job/text()") tag in Company class to get the required path.
but when unmarshalling i use the same Company class,i do not get the correct object values.Instead i get null values.
You need to include namespace information in the #XmlPath annotation.
package-info
Since your XML document has namespace qualification, you will need to leverage the package level #XmlSchema annotation to specify the namespace information.
#XmlSchema(
namespace="http://www.example.com/",
xmlns={
#XmlNs(namespaceURI = "http://www.example.com/", prefix = "foo")
}
)
package forum14848450;
import javax.xml.bind.annotation.*;
Company
In the #XmlPath mapping for the fragments of the XmlPath that are namespace qualified you need to leverage the prefixes you defined on the #XmlSchema annotation.
package forum14848450;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
public class Company {
#XmlPath("foo:employee/job/text()")
private String employeeJob;
#XmlPath("foo:employee/name/text()")
private String employeeName;
#XmlPath("foo:employee/age/text()")
private int employeeAge;
}
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 (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
The demo code below will unmarshal the document from your question, and then marshal it back to XML.
package forum14848450;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Company.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum14848450/input.xml");
Company company = (Company) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(company, System.out);
}
}
Output
Below is the output from running the demo code. Note how the output document uses the prefixes defined in the #XmlPath annotation.
<?xml version="1.0" encoding="UTF-8"?>
<foo:company xmlns:foo="http://www.example.com/">
<foo:employee>
<job>sogi</job>
<name>togi</name>
<age>22</age>
</foo:employee>
</foo:company>
For More Information
http://blog.bdoughan.com/2010/09/xpath-based-mapping-geocode-example.html

Jaxb order of the xmlns:xsi and xsi:noNamespaceSchemaLocation

I am using JAXB to create an xml.
Used
marshaller.setProperty(
Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION,
"bla-bla.xsd");
the xml being generated is
<Interface xsi:noNamespaceSchemaLocation="bla-bla.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
however the application that is parsing this xml for some reason is not parse it as they need it in this format
<Interface xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="bla-bla.xsd">
changing the target application is not an option :(
The following approach leveraging JAXB and StAX appears to give you the desired output, but since the order of attributes is not significant it is not guaranteed to always work.
import javax.xml.bind.*;
import javax.xml.stream.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Interface.class);
XMLOutputFactory xof = XMLOutputFactory.newFactory();
XMLStreamWriter xsw = xof.createXMLStreamWriter(System.out);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, "bla-bla.xsd");
marshaller.marshal(new Interface(), xsw);
}
}
Output
<?xml version="1.0"?><Interface xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="bla-bla.xsd"></Interface>

XmlSeeAlso and XmlRootElement names?

In the reference JAXB implementation is there anyway to get XmlSeeAlso to use the name= value from XmlRootElement?
The effect I want is for the type attribute to use the name= value rather than actual class name from XmlSeeAlso.
Is this possible is some other JAXB implementation?
Small example:
#XmlRootElement(name="some_item")
public class SomeItem{...}
#XmlSeeAlso({SomeItem.class})
public class Resource {...}
XML:
<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="some_item">
...
</resource>
Possible without a lot of effort?
About #XmlSeeAlso
The purpose of the #XmlSeeAlso annotation is just to let your JAXB (JSR-222) implementation know that when it is processing the metadata for Resource that it should also process the metadata for the SomeItem class. Some people mistakenly believe that it is related to mapping inheritance since that is the use case it is most often used with. Since the subclasses of a class can not be determined using Java reflection, #XmlSeeAlso is used to let the JAXB implementation know that mappings for the subclasses should also be created.
Below is an example of how you could support your use case:
Resource
The complex type name corresponding to a Java class is supplied via the #XmlType annotation.
package forum12288631;
import javax.xml.bind.annotation.XmlType;
#XmlType(name="some_item")
public class Resource {
}
Demo
The root element name can come from the #XmlRootElement annotation or can be supplied via an instance of JAXBElement. We will create an instance of JAXBElement and indicate that it is holding onto an instance of Object. When marshalled this will for the xsi:type attribute to be included in the output.
package forum12288631;
import javax.xml.bind.*;
import javax.xml.namespace.QName;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Resource.class);
Resource resource = new Resource();
JAXBElement<Object> jaxbElement = new JAXBElement<Object>(QName.valueOf("resource"), Object.class, resource);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(jaxbElement, System.out);
}
}
Output
The resulting XML has the root element supplied by the JAXBElement and the value of the xsi:type attribute comes from the #XmlType annotation on Resource.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="some_item"/>

How do I use JAXB #XmlValue on a subclass?

I want XML like this:
<simple>Foo</simple>
I can do this successfully via a JAXB class that looks like this:
#XmlRootElement(name="simple")
class Simple {
#XmlValue
public String contents;
}
But now I need to make the Simple class be a subclass of another class like so:
#XmlRootElement(name="simple")
class Simple extends OtherClass {
#XmlValue
public String contents;
}
That fails with #XmlValue is not allowed on a class that derives another class. I can't easily refactor the superclass away (because of the way we're using #XmlElementRef on a wrapper class). Is there a workaround that will let me annotate my subclass to generate that simple XML?
The accepted answer didn't work for me.
Everything is fine as described but I also needed to add the #XmlTransient to the superclass
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
This use case is supported by MOXy, and IMHO should be supported by the JAXB RI as well:
Simple
This class has a field mapped with #XmlValue and extends OtherClass:
package forum809827;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
#XmlRootElement(name="simple")
class Simple extends OtherClass {
#XmlValue
// #XmlValueExtension
// As of moxy 2.6, XmlValueExtension needs to be added for this to work
public String contents;
}
OtherClass
This is the super class. In MOXy the subclass can map a field/property with #XmlValue as long as the super class does not have any mappings to an XML element:
package forum809827;
import javax.xml.bind.annotation.XmlAttribute;
public class OtherClass {
#XmlAttribute
public String other;
}
Demo
package forum809827;
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(Simple.class);
Simple simple = new Simple();
simple.contents = "FOO";
simple.other = "BAR";
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(simple, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<simple xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" other="BAR">FOO</simple>
For More Information on Specifying MOXy as Your JAXB Provider
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
I was able to make this work by changing #XmlValue to #XmlMixed and changing the variable to a list. The resulting class should look like the following.
#XmlRootElement(name="simple")
class Simple extends OtherClass {
#XmlMixed
public List<String> contents;
}
This problem happened to me , and took me a little bit time.
Thanks to Blaise Doughan
I go through his blog and find the answer
you have to add a
jaxb.properties file with javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
in the same package in order to use MOXy
add moxy to your maven dependency or add moxy jar
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.moxy</artifactId>
<version>2.5.0</version>
</dependency>
then all set
I have sample here you can go though my project and take a look at
https://github.com/cicidi/HelloCCD/tree/master/Jaxb

Add/Override behavior on Jaxb generated classes by extending them

I have a web server responding with xml data and a client consuming it.
Both share the same domain code. One of the domain objects looks like this:
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
#XmlRootElement(name = "image")
public class Image {
private String filename;
private ImageTypeEnum type;
#XmlElement(name = "imageUri")
public String getAbsoluteUri() {
// some complex computation
return uri;
}
}
When I try to unmarshal the response from the server into this object, since there's no setter for absoluteUri, I don't have the imageUri in the class. So I extend it like this:
public class FEImage extends Image{
private String imageUri;
public String getAbsoluteUri() {
return imageUri;
}
public void setAbsoluteUri(String imageUri) {
this.imageUri = imageUri;
}
}
My ObjectFactory
#XmlRegistry
public class ObjectFactory {
public Image createImage(){
return new FEImage();
}
}
My code to unmarshal is here:
JAXBContext context = JAXBContext.newInstance(ObjectFactory.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty("com.sun.xml.bind.ObjectFactory",new ObjectFactory());
((JAXBElement)unmarshaller.unmarshal((InputStream) response.getEntity())).getValue();
However, the setAbsoluteUri doesn't seem to be getting called in FEImage while unmarshalling. When I add a dummy setAbsoluteUri in Image.java, everything works as expected.
Can someone tell me how can I cleanly extend from Image.java?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
A JAXB implementation is not required to use the ObjectFactory class when instantiating an object. You can configure instantiation to be done via a factory class using the #XmlType annotation:
#XmlType(factoryClass=ObjectFactory.class, factoryMethod="createImage")
public class Image {
private String filename;
private ImageTypeEnum type;
#XmlElement(name = "imageUri")
public String getAbsoluteUri() {
// some complex computation
return uri;
}
}
http://blog.bdoughan.com/2011/06/jaxb-and-factory-methods.html
If you do the above, then your JAXB implementation will still use the Image class to derive the metadata so it will not solve your problem. An alternate approach would be to use an XmlAdapter for this use case:
http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.html
Better still, when a property on your domain object does not have a setter, you can tell your
JAXB implementation (EclipseLink MOXy, Metro, Apache JaxMe, etc) to use field (instance variable) access instead using #XmlAccessorType(XmlAccessType.FIELD):
#XmlAccessorType(XmlAccessType.FIELD)
public class Image {
}
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
UPDATE #1
If you are not able to modify the domain objects, then you may be interested in MOXy's externalized metadata. This extension provides a means via XML to provide JAXB metadata for classes where you cannot modify the source.
For More Information
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
http://wiki.eclipse.org/EclipseLink/UserGuide/MOXy/Runtime/XML_Bindings
UPDATE #2 - Based on results of chat
Image
Below is the implementation of the Image class that I will use for this example. For the complex computation of getAbsoluteUri() I simply add the prefix "CDN" to the filename:
package forum7552310;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
#XmlRootElement(name = "image")
public class Image {
private String filename;
private ImageTypeEnum type;
#XmlElement(name = "imageUri")
public String getAbsoluteUri() {
return "CDN" + filename;
}
}
binding.xml
Below is the MOXy binding document I put together. In this file I do a few things:
Set XmlAccessorType to FIELD
Mark the absoluteURI property to be XmlTransient since we will be mapping the filename field instead.
Specify that an XmlAdapter will be used with the filename field. This is to apply the logic that is done in the getAbsoluteUri() method.
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum7552310">
<java-types>
<java-type name="Image" xml-accessor-type="FIELD">
<java-attributes>
<xml-element java-attribute="filename" name="imageUri">
<xml-java-type-adapter value="forum7552310.FileNameAdapter"/>
</xml-element>
<xml-transient java-attribute="absoluteUri"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
FileNameAdapter
Below is the implementation of the XmlAdapter that applies the same name algorithm as the getAbsoluteUri() method:
package forum7552310;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class FileNameAdapter extends XmlAdapter<String, String> {
#Override
public String marshal(String string) throws Exception {
return "CDN" + string;
}
#Override
public String unmarshal(String adaptedString) throws Exception {
return adaptedString.substring(3);
}
}
Demo
Below is the demo code demonstrating how to apply the binding file when creating the JAXBContext:
package forum7552310;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum7552310/binding.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Image.class}, properties);
File xml = new File("src/forum7552310/input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
Image image = (Image) unmarshaller.unmarshal(xml);
System.out.println(image.getAbsoluteUri());
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(image, System.out);
}
}
jaxb.properties
You need to include a file named jaxb.properties with the following contents in the same package as your Image class:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
input.xml
Here is the XML input I used:
<?xml version="1.0" encoding="UTF-8"?>
<image>
<imageUri>CDNURI</imageUri>
</image>
Output
And here is the output from running the demo code:
CDNURI
<?xml version="1.0" encoding="UTF-8"?>
<image>
<imageUri>CDNURI</imageUri>
</image>

Resources