JAXB unmarshal has always null value for nested object - java

I have a web-service, defined by writing its WSDL and underlaying XSD, and the java server code classes / java bindings were generated using JAXB/xjc.
Everything looks fine service is running properly... but for every request (looking well-formed after receiving when looking on log-output) the nested elements seem to be always null when accessing through my java code.
Can someone figure out why customerId.getCustomer() always returns null?
My XSD (partly):
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns:tip="http://example.org/tip" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://example.org/tip/pro">
<complexType name="id">
<attribute name="id" type="int" use="required"/>
<attribute name="name" type="string" use="optional"/>
</complexType>
<complexType name="customer_id">
<sequence>
<element name="customer" type="tip:id" minOccurs="0"/>
</sequence>
</complexType>
<element name="get_customer_request" type="tip:customer_id"/>
</schema>
The generated class CustomerId:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "customer_id", propOrder = {"customer"})
public class CustomerId {
protected Id customer;
public Id getCustomer() {
return customer;
}
public void setCustomer(Id value) {
this.customer = value;
}
}
The generated class for Id look similar, I don't think there is something special.
In my request handler I got the following extract:
Handler:
JAXBElement<?> request = requestHandler.unmarshallRequest(inputStream);
Object jaxbClass = request.getDeclaredType();
expectedClass = CustomerId.class;
// next line does not throw exception with given XML
if (jaxbClass != expectedClass) throw new IllegalArgumentException();
CustomerId customerId = (CustomerId)request.getValue();
if (customerId == null) {
logInfo("customerId: null");
} else if (customerId.getCustomer() == null) {
// this is the part that always will be executed... why?
logInfo("customerId.customer: null");
} else {
logInfo("customer id: " + customerId.getCustomer().getId());
// return mbean.getCustomer(customerId);
}
And finally an example request XML:
<?xml version="1.0" encoding="ISO-8859-1"?>
<m:get_customer_request xmlns:m="http://example.org/tip/pro">
<customer id="0" name="help"/>
</m:get_customer_request>
I stripped out SOAP envelope and body tags, since this is not causing any trouble.
Can anyone see, what I am doing wrong? (I am pretty sure, I do...)
Thanks for your effords!

PART 1
When I create a new Id and set customerId.customer with this, the full
output is
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<get_customer_request xmlns="example.com/tip/pro">
<customer name="xy" id="1"/>
</get_customer_request>
Based on this information it appears that your JAXB mappings expect the customer element to be in the example.com/tip/pro namespace, and your request document should be:
<?xml version="1.0" encoding="ISO-8859-1"?>
<m:get_customer_request xmlns:m="http://example.org/tip/pro">
<m:customer id="0" name="help"/>
</m:get_customer_request>
PART 2
When putting m: prefix to customer element in my request, the parser
complains that he found m:customer and expected customer.
This means that your XML schema does not match your mappings. If you expect the customer element to be in the namespace you can change your XML schema to the following:
<?xml version="1.0" encoding="UTF-8"?>
<schema
xmlns:tip="http://example.org/tip"
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.org/tip/pro"
elementFormDefault="qualified">
...
</schema>
For more information on JAXB and namespaces see:
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html

Related

ObjectFactory class generated from wsdl missing Factory Metods

I have searched for this in Internet but unable to find a answer.
I am new to SOAP services, If I am wrong at any point please correct me. I was trying to generate Java classes from wsdl using command line tool ** JAX-WS RI wsimport**. I was able to generate all Request and Response classes.
The ObjectFactory.java generated from wsdl was missing most of the methods.
employee.wsdl.jaxb.bnd.xml
<jaxb:bindings version="2.0"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb/xjc">
<jaxb:bindings>
<jaxb:globalBindings generateElementProperty="false">
<xjc:serializable>
</jaxb:globalBindings>
</jaxb:bindings>
</jaxb:bindings>
employee.wsdl.bnd.xml
<jaxws:bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://java.sun.com/xml/ns/jaxws"
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
wsdlLocation="../sp.wsdl">
<jaxws:enableWrapperStyle>false</jaxws:enableWrapperStyle>
</jaxws:bindings>
In jaxb.bnd.xml
While trying with <jaxb:globalBindings generateElementProperty="true"> Its able to generate all of the factory methods. But it was using JAXBElement<> wrapper for primititve data types like String,int,Long
While trying with <jaxb:globalBindings generateElementProperty="fasle"> Its able to generate the class without JAXBElement<> wrapper, but missing most of the factory methods.
cmd: wsimport META-INF/wsdl/employee.wsdl -keep -b META-INF/wsdl/bindings/employee.bnd.xml -b META-INF/wsdl/bindings/employee.jaxb.bnd.xml -wsdllocation META-INF/wsdl/employee.wsdl
Sample ObjectFactory.java
package xyz;
#XmlRegistry
class ObjectFactory {
private final static Qname _EmpDetailsSpecialEmpDetail_QNAME = new QName("", "scheduleEmp");
#XmlElementDecl(namespce = "", name="specialEmployee", scope= EmpDetails.class)
public SpecialEmpDetail EmpDetailsSpecialEmployee(SpecialEmpDetail value) {
return new SpecialEmpDetail(_EmpDetailsSpecialEmpDetail_QNAME, SpecialEmpDetail.class, EmpDetail.class, value);
}
EmpDetail.java
public class EmpDetail implements Serializable {
#XmlElementRef(name= "specialEmployee", required=false)
protected SpecialEmpDetail specialEmployee;
public SpcialEmpDetail getSpecialEmployee() {
return specialEmployee;
}
public SpcialEmpDetail setSpecialEmployee(SpcialEmpDetail value) {
this.specialEmployee = value;
}
}
SpecialEmpDetail.java
public class SpecialEmpDetail {
//local var
//getter and setter
.....
}
employee.wsdl
<?xml version="1.0" encoding="UTF-8">
<definitions name="psp"
targetNamespace="http://www.namespace.com/employee.wsdl"
.....
/>
<types>
<schmea targerNamespace = "http://www.namespace.com/employee.wsdl"
xmlns:SOAP-ENV = "http://www.w3.org/2003/05/soap-envelope"
xmlns:xsi = "http://www.w3.org/2003/05/XMLSchema-instance"
xmlns:xsd = "http://www.w3.org/2001/XMLSchema"
xmlns:emp = "http://www.namespace.com/employee/employee.xsd1"
<import namespace="http://www.namespace.com/employee.xsd1"/>
<import namespace="http://www.w3.org/2003/05/soap-encoding"/>
</schmea>
<schmea targerNamespace = "http://www.namespace.com/employee.xsd1"
xmlns:SOAP-ENV = "http://www.w3.org/2003/05/soap-envelope"
xmlns:xsi = "http://www.w3.org/2003/05/XMLSchema-instance"
xmlns:xsd = "http://www.w3.org/2001/XMLSchema"
xmlns:emp = "http://www.namespace.com/employee/employee.xsd1"
<import namespace="http://www.namespace.com/employee.wsdl"/>
<import namespace="http://www.w3.org/2003/05/soap-encoding"/>
<simpleType name="empName">
<restrictions base="xsd:string">
<minLength value="1"/>
<maxLength value="30"/>
</restrictions>
<simpleType name="EmpID">
<restrictions base="xsd:string">
<minLength value="0"/>
<maxLength value="25"/>
</restrictions>
</simpleType>
<complexType name="">
<sequence>
<element name="specialEmployee" type="emp:SpecialEmpDetail minOccurs="0" maxOccurs="1" />
</sequence>
</complexType>
.....
.........
</schema>
</types>
Here is the sample wsdl and java. I can able to generate SpecialEmpDetail.java & EmpDetail.java The ObjectFctory.java should create the mentioned function, but its failed to do so.... Using JAXBElement<> wrapper I can able to generate all functions but the problem here is its a complextType data so wrapping is fine, But String also wrapped this is not expected.
What may be problem?? How can I resolve it??
Thanks in Advance!

process xml with optional inner tags and values using jaxb

I've got an xml file that looks like this
<console-menu-entry index="2" text="Print Hello World">
<console-menu-entry index="1" text="Print Hello">
print 'Hello'
</console-menu-entry>
<console-menu-entry index="2" text="Print World">
print 'World'
</console-menu-entry>
</console-menu-entry>
Basically node <console-menu-entry> may have either tags inside it or some text value.
How to process it using jaxb? When I do like this it fails with
If a class has #XmlElement property, it cannot have #XmlValue property.
error.
My class looks like this
#XmlRootElement(name="console-menu-entry")
#XmlAccessorType(XmlAccessType.FIELD)
#ToString
public #Data class XmlConsoleMenuEntry {
#XmlAttribute
private String index;
#XmlAttribute
private String text;
#XmlValue
private String value;
#XmlElement(name="console-menu-entry")
private List<XmlConsoleMenuEntry> entries;
}
P.S. Using jaxb is not a requirement so if there is an approach using another library I am open to suggestions.
I would suggest to use JAXB but come from another end. Try to make use of xsd schema. This approach has a number of advantages:
Developing an XSD lets you understand your datamodel more deeply, detect possible flaws and see the data structure more clear.
You can use XSD to generate the parser which will help you to parse your xml (meeting that model) in a few lines of code
You can use XSD to create xml files (all the modern IDEs integrate XSD data in code-assistance mechanisms) so that you will get advice from IDE on which element would fit your datamodel in a particular place, which attribute is requited for a particular element (even which values are suitable for that attrubute) and many other usefule things
Below is the example of xsd schema which will let you generate parser which parses your example (you should only provide appropriate namespace for your xml elements)
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://some.your.schema"
xmlns:tns="http://some.your.schema"
elementFormDefault="qualified">
<xs:complexType name="ConsoleMenuEntry" mixed="true" >
<xs:sequence>
<xs:element ref="tns:console-menu-entry" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="index" type="xs:integer" use="required"/>
<xs:attribute name="text" type="xs:string" use="required"/>
</xs:complexType>
<xs:element name="console-menu-entry" type="tns:ConsoleMenuEntry"/>
</xs:schema>
You now can generate the parser files (Windows example)
"%JAVA_HOME%\bin\xjc" -d ../src -p your.app.generated test.xsd
Where -d ../src specifies the path on hard drive where your parser classes would be located, -p your.app.generated specifies the package for you generated parser classes and test.xsd is the schema file name
Here is the example of test.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<console-menu-entry xmlns="http://some.your.schema" index="1" text="Some new text">
<console-menu-entry index="1" text="some other text">
sdfsdkljf
</console-menu-entry>
<console-menu-entry index="2" text="some other text"/>
</console-menu-entry>
And the code that parses it:
public class Main {
public static void main(String[] args) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(ObjectFactory.class);
ConsoleMenuEntry rootEntry = ((JAXBElement<ConsoleMenuEntry>) jc.createUnmarshaller().unmarshal(new File("PATH_TO_FILE\\test.xml"))).getValue();
processMenuEntry(rootEntry);
}
private static void processMenuEntry(ConsoleMenuEntry menuEntry) {
System.out.println("Index (attr) = " + menuEntry.getIndex() + ", Text (attr) = '" + menuEntry.getText() + "'");
for (Serializable element : menuEntry.getContent()) {
if (element instanceof JAXBElement) {
processMenuEntry(((JAXBElement<ConsoleMenuEntry>) element).getValue());
} else if (element instanceof String) {
String innerText = element.toString().trim();
if (innerText.length() > 0) {
System.out.println("Inner text: '" + innerText);
}
}
}
}
}

Marshalling a complex type xml element using jaxb

I am running into multiple namespaces while marshalling a complex type Element(contains list of Element in it Example: device see below) using JAXB. Appreciate any help in getting the desired output as mentioned below.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Request xmlns="urn:ietf:params:xml:ns:geopriv:held"
xmlns:ns2="urn:ietf:params:xml:ns:geopriv:held:id"
xmlns:ns3="urn:ietf:params:xml:ns:geopriv:held:vendor" responseTime="10">
<ns2:device xmlns="urn:ietf:params:xml:ns:geopriv:held:id"
xmlns:ns2="urn:ietf:params:xml:ns:geopriv:held">
<uri>http://stackoverflow.com</uri>
</ns2:device>
Here device is a complex type element as defined in the extension schema. I am working with many schema's.
But the desired output without the extra namespaces around device complex type element is like this:
<?xml version="1.0" encoding="utf-8"?>
<Request xmlns="urn:ietf:params:xml:ns:geopriv:held" responseTime="10">
<device xmlns="urn:ietf:params:xml:ns:geopriv:held:id">
<uri>http://stackoverflow.com</uri>
</device>
</Request>
Device Schema:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="urn:ietf:params:xml:ns:geopriv:held:id"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:id="urn:ietf:params:xml:ns:geopriv:held:id"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="device" type="id:deviceIdentity" />
<xs:complexType name="deviceIdentity">
<xs:sequence>
<xs:any xmlns:id="urn:ietf:params:xml:ns:geopriv:held:id"
processContents="strict" minOccurs="0"
maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
Marshalling Code:
final JAXBElement<String> uri = objectFactory.createUri(uri);
Element elt = XmlMarshaller.getDomElement(uri, String.class);
final Device device = objectFactory.createDevice();
if (elt != null) {
device.getAnies().add(elt);
}
elt = XmlMarshaller.getDomElement(device, Device.class);
Request.getAnies().add(elt);
Element getDomElement(final Object object, final Class<?> clazz) throws JAXBException {
final JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
final Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
final DOMResult res = new DOMResult();
jaxbMarshaller.marshal(object, res);
final Element elt = ((Document) res.getNode()).getDocumentElement();
return (elt);
}

How do I get values returned in SOAP response assigned to the correct variable in the client?

I have the following in a WSDL file:
<definitions xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tns="urn:zimbraAdmin"
xmlns:zns="urn:zimbra"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:si="http://soapinterop.org/xsd"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="urn:zimbraAdmin">
<element name="AuthResponse">
<complexType>
<sequence>
<element name="authToken" type="xsd:string" />
<element name="lifetime" type="xsd:string"/>
<element ref="tns:a" maxOccurs="unbounded"/>
</sequence>
</complexType>
</element>
<message name="AuthResponse">
<part name="AuthResponse" element="tns:AuthResponse"/>
</message>
<operation name="AuthRequest">
<input message="tns:AuthRequest"/>
<output message="tns:AuthResponse"/>
</operation>
However, when the following request gets sent:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:AuthRequest xmlns:ns2="urn:zimbraAdmin" xmlns:ns3="urn:zimbra">
<name>admin</name>
<password>password</password>
</ns2:AuthRequest>
</S:Body>
</S:Envelope>
The following comes back:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<context xmlns="urn:zimbra">
<change token="2099" />
</context>
</soap:Header>
<soap:Body>
<AuthResponse xmlns="urn:zimbraAdmin">
<authToken>0_c728cf3ae4049ecb8fa5eb19f19397e4661e53a2_69643d33363a38373861353736372d356630632d343266352d623466372d6166666163383064326133643b6578703d31333a313337343533383131303638333b61646d696e3d313a313b747970653d363a7a696d6272613b</authToken>
<lifetime>43199997</lifetime>
<a n="zimbraIsDomainAdminAccount">false</a>
</AuthResponse>
</soap:Body>
</soap:Envelope>
The problem I have is that authToken and lifetime are not assigned (i.e. they come back null)
The code generated for AuthResponse (which was generated with cxf 2.7.5 wsdl2java) looks like follows:
#XmlRootElement(name = "AuthResponse")
public class AuthResponse {
#XmlElement(required = true)
protected String authToken;
#XmlElement(required = true)
protected String lifetime;
#XmlElement(namespace = "urn:zimbraAdmin", required = true)
protected List<A> a;
I believe that if it read as follows my problems would be solved:
#XmlRootElement(name = "AuthResponse")
public class AuthResponse {
#XmlElement(namespace = "urn:zimbraAdmin",required = true)
protected String authToken;
#XmlElement(namespace = "urn:zimbraAdmin",required = true)
protected String lifetime;
#XmlElement(namespace = "urn:zimbraAdmin", required = true)
protected List<A> a;
The question is, what do I need to change in the WSDL file so that the correct code for the client is generated?
EDIT
For reference, I am using the zimbra.wsdl available from http://blog.jeshurun.ca/wp-content/uploads/2011/12/zimbra.wsdl

From list of key values to attribute key=value

I have a question about JAXB. Basically what I have are these two classes:
Element {
String name
List<Attribute> attributes;
}
Attribute {
String key
String value
}
Of course with getters and setters, and with JAXB XmlRootElement.
The XML generated from this is:
<element>
<attributes>
<key>id</key>
<value>1</value>
</attributes>
<name>My Element</name>
</element>
But what I'm looking for is something more like this:
<element id="1">
<name>My Element</name>
</element>
That is, for each instance of Attribute, I want key=value (as an attribute)
Is this possible in JAXB?
Regards,
Morten
I do not think that it will work with a List. But there is an alternative using a
Map and
#XmlAnyAttribute
Your example:
#XmlRootElement
public static class Element
{
#XmlElement
String name;
#XmlAnyAttribute
Map<QName, Object> map;
}
{
//
Element element = new Element();
element.name = "a wonderful name";
element.map = new HashMap<QName, Object>();
element.map.put( new QName( "id" ), "1" );
element.map.put( new QName( "other" ), "2" );
}
Result of that:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<element id="1" other="2">
<name>a wonderful name</name>
</element>
Best regards!

Categories