When I try to generate a client from a wsdl document, I get a client that seems to have a lot of JAXBElement atributes, for instance
protected List<JAXBElement<?>> nameOrLinkingNameOrFamilyName;
I use soapUI to generate with apache cxf 2.3.3 as tool, also as config file the following:
<jaxb:bindings version="2.1"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jaxb:globalBindings generateElementProperty="false"/>
</jaxb:bindings>
As far as I saw this is related with the choice tags in the wsdl document.
thanks in advance
A JAXBElement will be generated for choice properties where multiple XML elements will correspond to the same Java class. This is in order to preserve information about the element since this can not be derived from the type of the value.
binding.xml
The following JAXB schema bindings file will ensure that a choice property is generated:
<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<globalBindings choiceContentProperty="true"/>
</bindings>
XML Schema That Will Produce Object Property
In this version of the XML schema, all of the XML elements will correspond to a different Java class:
<xsd:choice>
<xsd:element name="address" type="address"/>
<xsd:element name="phone-number" type="phoneNumber"/>
<xsd:element name="note" type="xsd:string"/>
</xsd:choice>
Since the value of the choice property is sufficient to uniquely identify the element, the property does not contain a JAXBElement to preserve this information:
#XmlElements({
#XmlElement(name = "address", type = Address.class),
#XmlElement(name = "phone-number", type = PhoneNumber.class),
#XmlElement(name = "note", type = String.class)
})
protected Object addressOrPhoneNumberOrNote;
XML Schema That Will Produce JAXBElement Property
Now we will modify the choice structure so that both the note and email methods will correspond to the String class.
<xsd:choice>
<xsd:element name="address" type="address"/>
<xsd:element name="phone-number" type="phoneNumber"/>
<xsd:element name="note" type="xsd:string"/>
<xsd:element name="email" type="xsd:string"/>
</xsd:choice>
Since the value of the choice property is no longer sufficient to uniquely identify the element, the property must contain a JAXBElement to preserve this information:
#XmlElementRefs({
#XmlElementRef(name = "phone-number", type = JAXBElement.class),
#XmlElementRef(name = "email", type = JAXBElement.class),
#XmlElementRef(name = "address", type = JAXBElement.class),
#XmlElementRef(name = "note", type = JAXBElement.class)
})
protected JAXBElement<?> addressOrPhoneNumberOrNote;
For More Information
http://blog.bdoughan.com/2011/04/xml-schema-to-java-xsd-choice.html
Related
I am trying to import this WSDL: https://gateway.monster.com:8443/bgwBroker
Among others, it includes this XSD: http://schemas.monster.com/current/xsd/Query.xsd, which contains this snippet:
<xsd:element name="Query">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Target" type="xsd:string"/>
<xsd:element name="SubTarget" type="xsd:string" minOccurs="0"/>
<xsd:element name="ResumeRestriction" minOccurs="0"/>
<xsd:element name="ReturnRestriction" minOccurs="0" maxOccurs="unbounded">
...
</xsd:element>
<xsd:element name="SelectBy" minOccurs="0" maxOccurs="unbounded">
...
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
...
<xsd:element name="ResumeRestriction">
...
</xsd:element>
As you can see, complex type ResumeRestriction and its fields is defined at the bottom, but referenced inside Query. There is a reference missing here. ReturnRestriction and SelectBy are defined inline and are generated correctly.
Using WSDL2Java, this generates the following annotated classes:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"target",
"subTarget",
"resumeRestriction",
"returnRestriction",
"selectBy"
})
#XmlRootElement(name = "Query")
public class Query {
#XmlElement(name = "Target", required = true)
protected String target;
#XmlElement(name = "SubTarget")
protected String subTarget;
#XmlElement(name = "ResumeRestriction")
protected Object resumeRestriction;
...
public void setResumeRestriction(Object value) {
this.resumeRestriction = value;
}
}
Making resumeRestriction of type Object instead of the right type.
If do have a generated version of ResumeRestriction just fine. They are just not being tied together:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"storeRenderedTextResume",
"doNotRenderSid",
"activeOnBoard"
})
#XmlRootElement(name = "ResumeRestriction")
public class ResumeRestriction {
...
}
If I now create a request using Spring Web Services, I cannot use the ResumeRestriction class, which I need in my request to set a specific flag.
ObjectFactory objectFactory = new ObjectFactory();
Query query = objectFactory.createQuery();
query.setTarget("JobSeekers");
ResumeRestriction resumeRestriction = objectFactory.createResumeRestriction();
resumeRestriction.setStoreRenderedTextResume(true);
query.setResumeRestriction(resumeRestriction);
getWebServiceTemplate().marshalSendAndReceive("https://gateway.monster.com:8443/bgwBroker", query);
This will throw the following error:
[com.sun.istack.SAXException2: Instance of "com.monster.schemas.monster.ResumeRestriction" is substituting "java.lang.Object", but "com.monster.schemas.monster.ResumeRestriction" is bound to an anonymous type.]
How can I solve this problem?
I obviously cannot change the WSDL or XSD, as I pull those in remotely. Is this is a bug on their side and if so, can I work around it?
You could specify the class reference in JAXB bindings file:
<jxb:bindings
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
version="2.1"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
jxb:extensionBindingPrefixes="xjc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemalocation="http://java.sun.com/xml/ns/jaxb
http://java.sun.com/xml/ns/jaxb"
>
<jxb:bindings >
<jxb:globalBindings typesafeEnumMaxMembers="3000"/>
</jxb:bindings>
<jxb:bindings schemaLocation="http://schemas.monster.com/current/xsd/Query.xsd">
<jxb:bindings node="//xs:element[#name='Query']//xs:element[#name='ResumeRestriction']">
<jxb:class ref="com.monster.schemas.monster.ResumeRestriction"/>
</jxb:bindings>
</jxb:bindings>
</jxb:bindings>
Consider this XSD:
<xsd:schema targetNamespace="http://foobar" xmlns:tns="http://foobar"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="IdAttribute">
<xsd:attribute name="id" type="xsd:token" />
</xsd:complexType>
<xsd:complexType name="FoobarType">
<xsd:sequence>
<xsd:element name="someIds" type="tns:IdAttribute" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="Foobar" type="tns:FoobarType" />
</xsd:schema>
which results in the following, generated Java class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "FoobarType", propOrder = {"someIds"})
public class FoobarType {
#XmlElement(required = true)
protected List<IdAttribute> someIds;
// ...
}
Because IdAttribute only contains one String (id), I want to map these Strings directly into my FoobarType for easier usage. Therefore I wrote an XmlAdapter:
public class IdAttributeAdapter extends XmlAdapter<IdAttribute, String> { ... }
I've edited the generated class manually to verify my XmlAdapter works as expected (it does):
#XmlElement(required = true)
#XmlJavaTypeAdapter(IdAttributeAdapter.class)
protected List<String> someIds;
Of course I want to have this little code change done during code generation, so I've added the following bindings file (src/main/xjb/bindings.xjb):
Attempt 1:
<jaxb:bindings version="2.0" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd">
<jaxb:bindings schemaLocation="../resources/xsd/foobar.xsd">
<jaxb:bindings node="//xsd:complexType[#name='IdAttribute']">
<xjc:javaType
name="java.lang.String"
adapter="org.foobar.IdAttributeAdapter" />
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
Result
com.sun.istack.SAXParseException2; systemId: file:/C:/dev/foobar/src/main/xjb/bindings.xjb;
lineNumber: 12; columnNumber: 91;
compiler was unable to honor this conversion customization. It is attached to a wrong place, or its inconsistent with other bindings.
Attempt 2 (from here):
<jaxb:globalBindings xmlns:foo="http://foobar" xsi:schemaLocation="http://foobar ../resources/xsd/foobar.xsd">
<xjc:javaType
name="java.lang.String"
xmlType="foo:IdAttribute"
adapter="org.foobar.IdAttributeAdapter" />
</jaxb:globalBindings>
Result
com.sun.istack.SAXParseException2; systemId: file:/C:/dev/projects/luna/oasch/src/main/xjb/bindings.xjb;
lineNumber: 8; columnNumber: 112;
undefined simple type "{http://foobar}IdAttribute".
I've tried a few variations, but they all resulted in similar errors. So what's the correct way to add an XmlApender using a bindings file?
What you try to do is wrong.
You should replace fully the xml type IdAttribute with the java type String.
You will have to work with generic dom.Element type and convert it <-> String.
Here is an exemple : JAXB #XmlAdapter for arbitrary XML for your case replace the java type Bar with String.
Other solution would be to add behavior to the generated class so that you add methods to your FoobarType to work as if it has a List of String
e.g. add methods
List<String> getIds()
void setIds(List<String>)
Here is a link that explain how you can do this : "Adding behavior"
I have a JAXB generated class PersonType with the properties name, phone, address. In the XSD I define that "phone" minoccurs="1".
How can I test programatically(i.e. via reflection, etc), in Java code, if the property "phone" is required or not in the XSD?
Later edit:
The JAXB generator creates a class without any "required", etc. attributes. So there's no annotation on the class or it's fields to indicate if it's required or not. However, there's the #XmlElement annotation on the field specifying the XSD equivalent element. So I guess the only solution is to inspect this annotation and then make an XPath or something against the XSD?
Thank you!
The #XmlElement annotation has a required property (false by default) that is used to indicate that an XML element is required (minOccurs > 0). You could use the java.lang.reflect APIs to get this value from the class.
XML Schema
In the XML schema below the foo element is required and the bar element is not.
<?xml version="1.0" encoding="UTF-8"?>
<schema
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org/schema"
xmlns:tns="http://www.example.org/schema"
elementFormDefault="qualified">
<complexType name="root">
<sequence>
<element name="foo" type="string"/>
<element name="bar" type="string" minOccurs="0"/>
</sequence>
</complexType>
</schema>
Generated Class
Below is the class that was generated from the XML schema. We see that the foo field is annotated with #XmlElement(required=true) anf the bar field is not.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "root", propOrder = {
"foo",
"bar"
})
public class Root {
#XmlElement(required = true)
protected String foo;
protected String bar;
}
Here is a extract from my XML schema:
<xsd:complexType name="MyType">
<xsd:sequence>
<xsd:element name="Numbers">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Number" minOccurs="1" maxOccurs="5" type="xsd:decimal"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
JAXB generates me the following class:
public class MyType {
protected MyType.Numbers numbers;
public static class Numbers {
protected List<BigDecimal> number;
}
}
But I'd like to ignore this intermediate class and have something like:
public class MyType {
protected List<BigDecimal> number;
}
Is that somehow possible?
Yes, that is possible with JAXB only with the help of external plugins, as this modification is actually change of the model. Have a look at #XmlElementWrapper plugin.
Note: The same question was already asked on this forum (How generate XMLElementWrapper annotation, Dealing with JAXB Collections, JAXB List Tag creating inner class). Please, use search first.
I have 2 sets of XSD's, one that generates RPC based calls, and another that defines the product specific methods. The RpcType object (generated by JAXB2) has a 'setRpcOperation' method defined by:
RpcType.setRpcOperation(JAXBElement<? extends RpcOperationType>)
That 'RpcOperation' object should be the 'specific product method' described above. One example is (also generated by JAXB2):
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "get-user-stats", propOrder = {
"reset"
})
public class GetUserStats {
protected Boolean reset;
/**
* Gets the value of the reset property.
*
* #return
* possible object is
* {#link Boolean }
*
*/
public Boolean isReset() {
return reset;
}
/**
* Sets the value of the reset property.
*
* #param value
* allowed object is
* {#link Boolean }
*
*/
public void setReset(Boolean value) {
this.reset = value;
}
}
Now, is it possible to create an instance of 'GetUserStatus' and add it to the RpcType object via setRpcOperation?
This type of scenario is common:
One schema to represent the message
Multiple schemas to represent the payload.
Below is one way this could be done:
Message Schema - message.xsd
Have one XML schema to represent your message envelope. Introduce one type to represent the body of the message. This type will be extended by the different payloads.
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org/message"
xmlns="http://www.example.org/message"
elementFormDefault="qualified">
<xsd:element name="message">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="body" type="body"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="body">
</xsd:complexType>
</xsd:schema>
Payload Schema - customer.xsd
This schema corresponds to a specific type of message payload. Notice how the customer type extends the body type from the message schema.
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
targetNamespace="http://www.example.org/customer"
xmlns="http://www.example.org/customer"
xmlns:m="http://www.example.org/message"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xsd:import schemaLocation="message.xsd" namespace="http://www.example.org/message"/>
<xsd:complexType name="customer">
<xsd:complexContent>
<xsd:extension base="m:body">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:schema>
org.example.message.package-info
This class was generated from message.xsd.
#javax.xml.bind.annotation.XmlSchema(namespace = "http://www.example.org/message", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package org.example.message;
org.example.message.Message
This class was generated from message.xsd.
package org.example.message;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"body"
})
#XmlRootElement(name = "message")
public class Message {
#XmlElement(required = true)
protected Body body;
public Body getBody() {
return body;
}
public void setBody(Body value) {
this.body = value;
}
}
org.example.message.Body
This class was generated from message.xsd.
package org.example.message;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "body")
public class Body {
}
org.example.customer.package-info
This class was generated from customer.xsd.
#javax.xml.bind.annotation.XmlSchema(namespace = "http://www.example.org/customer", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package org.example.customer;
org.example.customer.Customer
This class was generated from customer.xsd.
package org.example.customer;
import javax.xml.bind.annotation.*;
import org.example.message.Body;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "customer", propOrder = {
"name"
})
public class Customer extends Body {
#XmlElement(required = true)
protected String name;
public String getName() {
return name;
}
public void setName(String value) {
this.name = value;
}
}
Demo Class
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import org.example.customer.*;
import org.example.message.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Message.class, Customer.class);
Message message = new Message();
Customer customer = new Customer();
customer.setName("Jane Doe");
message.setBody(customer);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(message, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<message xmlns="http://www.example.org/message" xmlns:ns2="http://www.example.org/customer">
<body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns2:customer">
<ns2:name>Jane Doe</ns2:name>
</body>
</message>
EDIT #1
In response to your second question (Cast JAXB2 generated object to JAXBElement?)
I don't see where the JAXBElement comes into play with this example. I am able to run the following code:
import generated.GetFailedLoginCount;
import ietf.params.xml.ns.netconf.base._1.RpcType;
public class Demo {
public static void main(String[] args) {
RpcType rpc = new RpcType();
rpc.setMessageId("123");
GetFailedLoginCount rpcOperation = new GetFailedLoginCount();
rpc.setRpcOperation(rpcOperation);
}
}
EDIT #2
After changing the import to import to http://www.iana.org/assignments/xml-registry/schema/netconf.xsd I'm seeing the same behaviour as you.
To set the property correctly you will need to do something like:
RpcType rpc = new RpcType();
GetFailedLoginCount rpcOperation = new GetFailedLoginCount();
rpcOperation.setReset(true);
JAXBElement<GetFailedLoginCount> rpcOperationJE = new JAXBElement(new QName("foo"), GetFailedLoginCount.class, rpcOperation);
rpc.setRpcOperation(rpcOperationJE);
JAXBElement supplies the element name for the GetFailedLoginCount value. This is required because the element corresponding to the rpcOperation property is substitutable:
<xs:element name="get-config" type="getConfigType" substitutionGroup="rpcOperation" />
In your schema that imports netconf.xsd you should have an element of type "get-failed-login-count" that can be substituted for "rpcOperation". This will be supplied as a QName to the JAXBElement. Since we used element name "foo" above the schema update would look like:
<xs:element name="foo" type="get-failed-login-count" substitutionGroup="rpcOperation" />
Ok, so here is a subset of what I am trying to do. The above example was extremely helpful, but I still am not able to create a JAXBElement:
Base envelope can be found: http://www.iana.org/assignments/xml-registry/schema/netconf.xsd
Payload for rpcOperationType (I added the ** lines):
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:dmi="http://xml.juniper.net/dmi"
** xmlns:netconf="urn:ietf:params:xml:ns:netconf:base:1.0"
>
**<xs:import schemaLocation="netconf.xsd" namespace="urn:ietf:params:xml:ns:netconf:base:1.0"/>
<!-- get-failed-login-count -->
<xs:complexType name="get-failed-login-count">
**<xs:complexContent>
** <xs:extension base="netconf:rpcOperationType">
<xs:annotation>
<xs:appinfo>
<dmi:rpc-info>
<name>Get failed login count for Authentication failure and Exceeded user</name>
<description>
This command returns the Number of Logins refused due to exceeding allowed limits and Auth failure (24 hour moving window)
</description>
<rpc-reply-tag>failed-login-count</rpc-reply-tag>
</dmi:rpc-info>
</xs:appinfo>
</xs:annotation>
<xs:sequence>
<xs:element name="reset" type="xs:boolean" minOccurs="0">
<xs:annotation>
<xs:appinfo>
<dmi:param-info>
<name>Reset Stats</name>
<description>
This will govern the reseting of this statistics data. By default, the data is not reset.
</description>
</dmi:param-info>
</xs:appinfo>
</xs:annotation>
</xs:element>
</xs:sequence>
** </xs:extension>
**</xs:complexContent>
</xs:complexType>
Now, the generated GetFailedLogin class extends RpcOperationType, but I am not sure how to set it in the RpcType object:
RpcType rpc = new RpcType();
rpc.setMessageId("123");
GetFailedLoginCount rpcOperation = new GetFailedLoginCount();
rpc.setRpcOperation(); // Expects JAXBElement<? Extends RpcOperationType>