JAXB xjc:simple binding does not generate #XmlRootElement for base class - java

I am using JAXB 2.0. I have various elements and types defined in an XSD file. Here's an example:
<xs:element name="Person" type="Person" />
<xs:complexType name="Person">
<xs:attribute name="name" type="xs:string"/>
</xs:complexType>
<xs:element name="Musician" type="Musician"/>
<xs:complexType name="Musician">
<xs:complexContent>
<xs:extension base="Person">
<xs:attribute name="instrument" type="xs:string"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="People" type="People"/>
<xs:complexType name="People">
<xs:sequence>
<xs:element name="person" type="Person" minOccurs="0" maxOccurs="Unbounded/>
</xs:sequence>
</xs:complexType>
So as you can see from the above schema example, we have a Person, who has a name, and a Musician, who is also a Person (though this may be subject to some debate, but that's for another forum). There is also a People element, which is essentially a collection of Person types.
I have the following in a bindings file:
<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:version="2.0"
xmlns:xjc= "http://java.sun.com/xml/ns/jaxb/xjc"
jaxb:extensionBindingPrefixes="xjc">
<jaxb:globalBindings optionalProperty="wrapper">
<xjc:simple/>
</jaxb:globalBindings>
My intended use of these objects is:
A Person may appear as a solitary Marshalled element, or may be a part of a People object
If a Person is part of a People object, then the xsi:type should indicate whether it's just a normal Person or a Musician.
So I need the generated Java classes to contain both an #XmlRootElement annotation as well as an #XmlType annotation. The xjc:simple binding creates both annotations for a Musician, but only creates the #XmlType for Person. So when I Marshall a Person object, all I get is:
<?xml version="1.0" encoding="UTF-8"?>
Whereas what I would like to see is:
<?xml version="1.0" encoding="UTF-8"?>
<Person name="John Doe"/>
For a People object, I want to see:
<?xml version="1.0" encoding="UTF-8"?>
<People>
<person name="John Doe" xsi:type="Person"/>
<person name="Keith Richards" xsi:type="Musician"/>
</People>
I have read about the simple binding with xjc, and it works with all the lowest levels in an inheritance hierarchy. However, the base classes end up without an #XmlRootElement annotation. For the use case I'm working on, it's imperative that base classes can be Marshalled as both a top-level element and as a member of other elements. Any suggestions would be welcome.

https://github.com/highsource/jaxb2-annotate-plugin could be used
<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:annox="http://annox.dev.java.net"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:version="2.1"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
jaxb:extensionBindingPrefixes="xjc annox">
<jaxb:bindings node="//xs:complexType[#name='Person']">
<annox:annotate>
<annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="person"/>
</annox:annotate>
</jaxb:bindings>
</jaxb:bindings>

Related

XPath evaluation of results in an empty target node

My wsdl specification contains imported XSD schema.
wsdl file looks like below
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" ....>
<wsdl:types>
<xsd:schema targetNamespace="http://tempuri.org/Imports">
<xsd:import schemaLocation="https://abcserver.com/v2/two-way-ssl/MyService.svc?xsd=xsd2" namespace="http://tempuri.org/"/>
<xsd:import schemaLocation="https://abcserver.com/v2/two-way-ssl/MyService.svcc?xsd=xsd4" namespace="http://schemas.datacontract.org/2004/07/System.Web.Services.Protocols"/>
<xsd:import schemaLocation="https://abcserver.com/v2/two-way-ssl/MyService.svc?xsd=xsd0" namespace="http://schemas.microsoft.com/2003/10/Serialization/"/>
<xsd:import schemaLocation="https://abcserver.com/v2/two-way-ssl/MyService.svc?xsd=xsd1" namespace="http://schemas.datacontract.org/2004/07/ABCUser.Web.Services"/>
<xsd:import schemaLocation="https://abcserver.com/v2/two-way-ssl/MyService.svc?xsd=xsd3" namespace="http://schemas.datacontract.org/2004/07/ABCUser.Web.ServiceModels"/>
<xsd:import schemaLocation="https://abcserver.com/v2/two-way-ssl/MyService.svc?xsd=xsd5" namespace="http://schemas.datacontract.org/2004/07/System"/>
<xsd:import schemaLocation="https://abcserver.com/v2/two-way-ssl/MyService.svc?xsd=xsd6" namespace="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
<xsd:import schemaLocation="https://abcserver.com/v2/two-way-ssl/MyService.svc?xsd=xsd7" namespace="http://schemas.datacontract.org/2004/07/System.Collections.Generic"/>
<xsd:import schemaLocation="https://abcserver.com/v2/two-way-ssl/MyService.svc?xsd=xsd8" namespace="http://schemas.datacontract.org/2004/07/ABC.Fs.UIEntities"/>
<xsd:import schemaLocation="https://abcserver.com/v2/two-way-ssl/MyService.svc?xsd=xsd9"/>
</xsd:schema>
</wsdl:types>
.......
</wsdl:definitions>
My jaxb bindings file look like below:
<jaxws:bindings xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.1"
wsdlLocation="https://abcserver.com/v2/two-way-ssl/MyService.svc?wsdl">
<enableWrapperStyle>true</enableWrapperStyle>
<enableAsyncMapping>false</enableAsyncMapping>
<jaxws:bindings node="wsdl:definitions/wsdl:types/xs:schema/xs:complexType[#name='Node']/xs:sequence/xs:element[#name='Type']">
<jaxb:class name="NodeTypeString"/>
</jaxws:bindings>
</jaxws:bindings>
If I don't use bindings.xml, I get following error while generating java classes through wsimport
[ERROR] Two declarations cause a collision in the ObjectFactory class.
line 1 of https://abcserver.com/v2/two-way-ssl/MyService.svc?xsd=xsd3
[ERROR] (Related to above error) This is the other declaration.
line 1 of https://abcserver.com/v2/two-way-ssl/MyService.svc?xsd=xsd3
Schema it is complaining about looks like below
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.datacontract.org/2004/07/ABCUser.Web.ServiceModels" elementFormDefault="qualified" targetNamespace="http://schemas.datacontract.org/2004/07/ABCUser.Web.ServiceModels">
<xs:import schemaLocation="https://abcserver.com/v2/two-way-ssl/MyService.svc?xsd=xsd6" namespace="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
.......
<xs:complexType name="Node">
<xs:sequence>
<xs:element minOccurs="0" name="Description" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="Name" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="Type" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="Users" nillable="true" type="tns:ArrayOfUser"/>
</xs:sequence>
</xs:complexType>
<xs:element name="Node" nillable="true" type="tns:Node"/>
......
But now when I use bindings.xml, I get the following error
[ERROR] XPath evaluation of "wsdl:definitions/wsdl:types/xs:schema/xs:complexType[#name='Node']/xs:sequence/xs:element[#name='Type']" results in an empty target node
line 8 of file:/E:/projects/codegeneration/bindings.xml
What am I missing? I am using wsimport to generate the classes.
<xsd:import> does not actually change the WSDL file’s XML document tree. You have elements matching wsdl:definitions/wsdl:types/xs:schema/xs:import, not wsdl:definitions/wsdl:types/xs:schema/xs:complexType/xs:sequence/xs:element.
The JAX-WS specification’s “Customizations” chapter says:
Additionally, jaxb:bindings MAY appear inside a JAX-WS external binding file as a child of a jaxws:bindings element whose node attribute points to a xs:schema element inside a WSDL document. When the schema is processed, the outcome MUST be as if the jaxb:bindings element was inlined inside the schema document as an annotation on the schema component.
While processing a JAXB binding declaration (i.e. a jaxb:bindings element) for a schema document embedded inside a WSDL document, all XPath expressions that appear inside it MUST be interpreted as if the containing xs:schema element was the root of a standalone schema document.
So, your inner jaxws:bindings element must contain the XPath of the xs:schema element, not any of its descendants:
<jaxws:bindings node="wsdl:definitions/wsdl:types/xs:schema">
<jaxb:bindings node="xs:complexType[#name='Node']/xs:sequence/xs:element[#name='Type']">
<jaxb:class name="NodeTypeString"/>
</jaxb:bindings>
</jaxws:bindings>
I’m not sure if the above will actually work with a schema that uses <xsd:import>. You may have to refer to the imported schema explicitly:
<jaxws:bindings node="wsdl:definitions/wsdl:types/xs:schema">
<jaxb:bindings schemaLocation="https://abcserver.com/v2/two-way-ssl/MyService.svc?xsd=xsd2">
<jaxb:bindings node="xs:complexType[#name='Node']/xs:sequence/xs:element[#name='Type']">
<jaxb:class name="NodeTypeString"/>
</jaxb:bindings>
</jaxb:bindings>
</jaxws:bindings>
I got two solutions for this issue I was facing.
I streamlined the wsdl schema by adding imported schema in it and removing import statements.
First build an episode using xjc for imported schema
xjc -episode myschema.episode myschema.xsd
And then use that episode as a binding in java classes generation through wsimport
wsimport mywsdl.wsdl -b myschema.episode
More about 2nd solution here

JAXB with XPATH Flexibility to modify Objects at runtime

I want to modify JAXB loaded objects (from XML) so that i can marshall them back to the disk with updated XML.
Now lets take an example:
<Customers>
<Customer id="1" name="Jack">
<Address type="Residence">
<FirstLine>1 saxon Court</FirstLine>
<City>CY</City>
</Address>
</Customer>
<Customer id="2" name="Iain">
<Address type="Residence">
<FirstLine>104 Bank Road</FirstLine>
<City>NY</City>
</Address>
</Customer>
</Customers>
Now as I have multiple customers, I want to use XPATH functionality to get the handle of the Customer[#id=2] object where I can add/update an address.
If I will not use the XPATH like functionality then JAXB generated classes will have "List<Customer> customer" and I will have to iterate through the list of customer to match with my desirable Customer[#id=2].
Can anyone give me idea how to get the Object instance handle for the JAXB generated objects using XPATH, so I can marshall it back to update the actual XML on the disk.
If it cannot be done through JAXB, then what can be alternative solution to read and write XMLs using java Objects with XPATH flexibility.
Updated Question with Sample code:
Following is the sample code which shows what i want to achieve with Moxy/JAXB.
package org.soc.test.customers.moxy;
import java.io.File;
import java.util.List;
import javax.xml.bind.*;
public class UnmarshalDemo {
public static void main(String[] args) throws Exception {
org.eclipse.persistence.jaxb.JAXBContext jc = (org.eclipse.persistence.jaxb.JAXBContext) JAXBContext.newInstance(Customer.class);
File instanceDoc = new File("input.xml");
Customer customer = (Customer) jc.createUnmarshaller() .unmarshal(instanceDoc);
List<PhoneNumber> phones = jc.getValueByXPath(customer, "phone-number[#id=\"12\"]", null, List.class);
String customerId = jc.getValueByXPath(customer, "#id", null, String.class);
System.out.println("Customerid " + customerId + " , phone " + (phones==null?"0":phones.size()));
jc.setValueByXPath(customer, "phone-number[#id=\"12\"]/area-code/text()", null, "555");
jc.createMarshaller().marshal(customer, System.out);
}
}
Input XML:
<?xml version="1.0" encoding="UTF-8"?>
<customer id="1141">
<first-name>Jon</first-name>
<last-name>Smith</last-name>
<phone-number id="11">
<area-code>515</area-code>
<number>2726652</number>
</phone-number>
<phone-number id="12">
<area-code>515</area-code>
<number>2726652</number>
</phone-number>
</customer>
XSD:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="unqualified"
attributeFormDefault="unqualified">
<xs:element name="customer">
<xs:complexType>
<xs:sequence>
<xs:element name="first-name" type="stringMaxSize5"/>
<xs:element name="last-name" type="stringMaxSize5"/>
<xs:element ref="phone-number" maxOccurs="2"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string"/>
</xs:complexType>
</xs:element>
<xs:element name="phone-number">
<xs:complexType>
<xs:sequence>
<xs:element name="area-code" type="stringMaxSize5"/>
<xs:element name="number" type="xs:string"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string"/>
</xs:complexType>
</xs:element>
<xs:simpleType name="stringMaxSize5">
<xs:restriction base="xs:string">
<xs:maxLength value="5"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
jaxb.properties:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Output:
Customerid 1141 , phone 0
I am expecting the list size of the phone number to be 1 but it is returning null.
Hope this helps in understanding about the problem that I facing.
Thanks
Venkat
It's not supported with Moxy. The org.eclipse.persistence.internal.oxm.Context only supports numeric indexes in square brackets. While the XPathFragment understands it and creates appropriate predicates, the Context ignores those as far as searching for a match goes. I'd either raise a bug, or look for another tool.
See the Context source code (Commit 7cedaac6cdf9384ae9a06129d6f9abd607f9e3c4, Line 371 onwards) for exactly what's happening.

JAXB support for SOAP style arrayType

I'm trying to make a new version of a server that previously used Axis 1.4 to respond to SOAP RPC requests using Spring-WS. I have a few of the RPC calls working, but I'm stuck trying to satisfy a request that expects a SOAP body that looks like this:
<rpcCallResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<responseElement soapenc:arrayType="xsd:string[5]"
xsi:type="soapenc:Array"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<responseElement xsi:type="xsd:string">val1</responseElement>
<responseElement xsi:type="xsd:string">val2</responseElement>
<responseElement xsi:type="xsd:string">val3</responseElement>
<responseElement xsi:type="xsd:string" xsi:nil="true"/>
<responseElement xsi:type="xsd:string" xsi:nil="true"/>
</responseElement>
</rpcCallResponse>
I'm struggling to write the XML schema for this, and to get the JAXB marshaller to shove the xsi:type annotations into the response.
What's the correct XML schema to use/set of annotations to use to get this to marhsal (Java -> XML) correctly?
One solution I found that works for getting the arrayType added is to, instead of deriving from the http://schemas.xmlsoap.org/soap/encoding/ schema, use a custom schema of the form:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://schemas.xmlsoap.org/soap/encoding/"
elementFormDefault="qualified">
<xs:attribute name="arrayType" type="xs:QName" />
</xs:schema>
...which replaces the type of the arrayType attribute with an xs:QName (vs. the actual type, which is just an xs:string). The advantage of using a QName seems to be that JAXB will take the QName's namespace and shove it onto the element when serialization happens -- which was the main blocker on getting a working schema above.
The schema for above now looks something like:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:encoding="http://schemas.xmlsoap.org/soap/encoding/"
targetNamespace="http://foo.com/bar"
elementFormDefault="qualified">
<xs:import namespace="http://schemas.xmlsoap.org/soap/encoding/" schemaLocation="soapenc.xsd" />
<xs:element name="rpcCallResponse">
<xs:complexType>
<xs:element name="responseElement">
<xs:complexType>
<xs:sequence>
<xs:element name="responseElement" maxOccurs="5" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:attribute ref="soapenc:arrayType" />
</xs:complexType>
</xs:element>
</xs:schema>

JAXB - Generate schema. Complex Type custom attribute

I need to generate following schema from java class using JAXB.
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xdb="http://xmlns.oracle.com/xdb">
<xs:element name="test" type="test"/>
<xs:complexType name="testName" xdb:SQLType="WEBY_TEST_NAME">
<xs:sequence>
<xs:element minOccurs="0" name="date" type="xs:dateTime"/>
<xs:element name="id" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
How to add xdb:SQLType="WEBY_TEST_NAME" into complexType element using jaxb annotations ?
i done same try for generating the schema for different tags, but the names which include reserve words or else need to declare as
#XmlElement(name="class")
public String getClasss() {
return classs;
}
in pojo, so at time of coding it uses name which we provide, and in java program it uses the declared variables.
may be your declaration become
#XmlElement(name="xdb:SQLType")
public String getxdbSQLType() {
return xdbSQLType;
}

Convert xs:string to java.util.UUID in jaxb

In jaxb, how do you convert a string in xsd to java.util.UUID? Is there a built-in data type converter or do I have to create my own custom converter?
This is much easier to do if you start with Java classes and use JAXB annotations. However, to do this using schema you must use a custom bindings file. Here is an example:
Schema: (example.xsd)
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.com"
xmlns="http://www.example.com"
elementFormDefault="qualified">
<xs:simpleType name="uuid-type">
<xs:restriction base="xs:string">
<xs:pattern value=".*"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="example-type">
<xs:all>
<xs:element name="uuid" type="uuid-type"/>
</xs:all>
</xs:complexType>
<xs:element name="example" type="example-type"/>
</xs:schema>
Bindings: (bindings.xjb) (Note that for brevity in printMethod and parseMethod I assumed that the UuidConverter class was in the default package. These should be fully qualified in reality. So if UuidConverter where in package com.foo.bar then the values should be like com.foo.bar.UuidConverter.parse and com.foo.bar.UuidConverter.print
<!-- Modify the schema location to be a path or url -->
<jxb:bindings version="1.0"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
node="/xs:schema"
schemaLocation="example.xsd">
<!-- Modify this XPATH to suit your needs! -->
<jxb:bindings node="//xs:simpleType[#name='uuid-type']">
<jxb:javaType name=" java.util.UUID"
parseMethod="UuidConverter.parse"
printMethod="UuidConverter.print"/>
</jxb:bindings>
</jxb:bindings>
UuidConverter.java:
import java.util.UUID;
public class UuidConverter {
public static UUID parse(String xmlValue) {
return UUID.fromString(xmlValue);
}
public static String print(UUID value) {
return value.toString();
}
}
Sadly I can't point you to a good reference because its really not documented well. There are bits and pieces of how it all works spread out in blog posts. Took me a few hours to make this work the first time. :-/
Create a simple converter yourself:
UUID.fromString(String uuid);
http://docs.oracle.com/javase/6/docs/api/java/util/UUID.html

Categories

Resources