How to identify the relation between elements in JAXB - jaxb

Is there intelligent way to identify relation between elements using JAXB.
For example: If element SMTP is referred/used in the element Notification and element Configuration
<element name="SMTP">
<complexType>
<sequence>
<element name="fromEmailAddress" type="string"/>
<element name="hostName" type="string"/>
<element name="portNumber" type="string"/>
</sequence>
</complexType>
</element>
<element name="Notification">
<complexType>
<sequence>
<element ref="tns:SMTP"/>
</sequence>
</complexType>
</element>
<element name="Configuration">
<complexType>
<sequence>
<element ref="tns:SMTP"/>
</sequence>
</complexType>
</element>
Is there way to identify this relation/dependancy(SMTP with Notification & Configuration) using JAXB(iam using JAXB to generate classes for the above elements present in my XSD).If it is, sample java code will be helpful
Thanks
Ravi

The Configuration and Notification classes will be generated with an SMTP property:
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 = "", propOrder = {
"smtp"
})
#XmlRootElement(name = "Configuration")
public class Configuration {
#XmlElement(name = "SMTP", namespace = "urn:example", required = true)
protected SMTP smtp;
public SMTP getSMTP() {
return smtp;
}
public void setSMTP(SMTP value) {
this.smtp = value;
}
}
If you are looking to make this a bidirectional relationship you can leverage the #XmlInverseReference extension in EclipseLink JAXB (I'm the tech lead). For more information see:
http://bdoughan.blogspot.com/2010/07/jpa-entities-to-xml-bidirectional.html

Related

JAXB Unmarshalling X,X*

I am having a problem unmarshalling this class from XML.
The problem is caused by the two elements called 'propertyset'. What actually happens is that the first propertyset is unmarshalled correctly then it is immediately overwritten with the second, if it exists...
Is there any annotation/configuration that I can use to successfully unmarshal this XML?
/**
* <p>Java class for a element declaration.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* <element name="a">
* <complexType>
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element ref="propertyset"/>
* <element ref="propertyset" minOccurs="0"/>
* </sequence>
* <attribute name="id" type="{http://www.w3.org/2001/XMLSchema}long" />
* <attribute name="description" type="{http://www.w3.org/2001/XMLSchema}string" />
* </restriction>
* </complexContent>
* </complexType>
* </element>
* </pre>
*
*
*/
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"propertyset",
"systempropertyset"
}, factoryMethod = "createAEx", factoryClass = ObjectFactoryEx.class)
#XmlRootElement(name = "a")
public class A
{
#XmlElement(required = true, type = B.class)
protected B propertyset;
#XmlElement(name = "propertyset", type = B.class)
protected B systempropertyset;
...
}
I have now moved on to using Moxy. With this I can use #XmlPath("propertyset[1]") and #XmlPath("propertyset[2]")
This works as advertised!

spring integration :SpelEvaluationException: EL1004E:(pos 8): Method call: Method getResponseForSample(javax.xml.bind.JAXBElement)

I am new in Spring integration and facing the issue when unmarshalling the SOAP request. I have the below sample xsd:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://tempuri.org/" xmlns:tns="http://tempuri.org/"
version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="FFSampleRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="Timestamp" type="xs:string" minOccurs="1"
form="qualified" />
<xs:element name="SampleType" type="xs:int" form="qualified" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="FFSampleResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="Timestamp" type="xs:string" minOccurs="1" />
<xs:element name="SampleType" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="getResponseForSample" type="tns:getResponseForSample" />
<xs:element name="getResponseForSampleResponse" type="tns:getResponseForSampleResponse" />
<xs:complexType name="getResponseForSample">
<xs:sequence>
<xs:element name="arg0" minOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="Timestamp" type="xs:string"
minOccurs="1" form="qualified" />
<xs:element name="SampleType" type="xs:int" form="qualified" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="getResponseForSampleResponse">
<xs:sequence>
<xs:element name="return" minOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="Timestamp" type="xs:string"
minOccurs="1" />
<xs:element name="SampleType" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
Now I have created the java files using the jaxbmarshaller xjc compiler, below is objectFactory file.other files generated are FFSampleRequest, FFSampleResponse, GetResponseForSample, GetResponseForSampleResponse, package-info. put all these files at
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2
// See http://java.sun.com/xml/jaxb
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2014.07.07 at 06:00:25 PM GST
//
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
private final static QName _GetResponseForSample_QNAME = new QName("http://tempuri.org/", "getResponseForSample");
private final static QName _GetResponseForSampleResponse_QNAME = new QName("http://tempuri.org/", "getResponseForSampleResponse");
public ObjectFactory() {
}
/**
* Create an instance of {#link GetResponseForSample }
*
*/
public GetResponseForSample createGetResponseForSample() {
return new GetResponseForSample();
}
/**
* Create an instance of {#link GetResponseForSampleResponse }
*
*/
public GetResponseForSampleResponse createGetResponseForSampleResponse() {
return new GetResponseForSampleResponse();
}
/**
* Create an instance of {#link FFSampleRequest }
*
*/
public FFSampleRequest createFFSampleRequest() {
return new FFSampleRequest();
}
/**
* Create an instance of {#link FFSampleResponse }
*
*/
public FFSampleResponse createFFSampleResponse() {
return new FFSampleResponse();
}
/**
* Create an instance of {#link GetResponseForSample.Arg0 }
*
*/
public GetResponseForSample.Arg0 createGetResponseForSampleArg0() {
return new GetResponseForSample.Arg0();
}
/**
* Create an instance of {#link GetResponseForSampleResponse.Return }
*
*/
public GetResponseForSampleResponse.Return createGetResponseForSampleResponseReturn() {
return new GetResponseForSampleResponse.Return();
}
/**
* Create an instance of {#link JAXBElement }{#code <}{#link GetResponseForSample }{#code >}}
*
*/
#XmlElementDecl(namespace = "http://tempuri.org/", name = "getResponseForSample")
public JAXBElement<GetResponseForSample> createGetResponseForSample(GetResponseForSample value) {
return new JAXBElement<GetResponseForSample>(_GetResponseForSample_QNAME, GetResponseForSample.class, null, value);
}
/**
* Create an instance of {#link JAXBElement }{#code <}{#link GetResponseForSampleResponse }{#code >}}
*
*/
#XmlElementDecl(namespace = "http://tempuri.org/", name = "getResponseForSampleResponse")
public JAXBElement<GetResponseForSampleResponse> createGetResponseForSampleResponse(GetResponseForSampleResponse value) {
return new JAXBElement<GetResponseForSampleResponse>(_GetResponseForSampleResponse_QNAME, GetResponseForSampleResponse.class, null, value);
}
}
web.xml file to handle the soap request:
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/spring-ws-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
and used the below settings in the configuration file for calling the service method for this request:
<int:channel id="FFSampleRequestChannel"/>
<int-ws:inbound-gateway id="ws-ffsample-gateway" request-channel="FFSampleRequestChannel" marshaller="marshaller" unmarshaller="marshaller"/>
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPath" value="com.java.MyChannel.model" />
</bean>
<int:service-activator input-channel="FFSampleRequestChannel">
<bean class="com.java.MyChannel.serviceImpl.FFSampleImpl">
<constructor-arg ref = "ffSampleRequest"></constructor-arg>
<bean id="ffSampleRequest" class="com.java.MyChannel.model.FFSampleRequest"></bean>
<bean id="ffSampleResponse" class="com.java.MyChannel.model.FFSampleResponse"></bean>
my soap request hits service class successfully but I am getting the exception.
/**
* #author Vinay Agrawal
*/
#XmlRegistry
public class FFSampleImpl implements FFSampleInterface
{
#Autowired
FFSampleRequest request;
#Autowired
FFSampleJdbc ffSampleJdbc;
public FFSampleImpl() {
}
public FFSampleImpl(FFSampleRequest request) {
super();
this.request = request;
}
#Override
#PayloadRoot(localPart = "getResponseForSample" , namespace = "http://tempuri.org/")
#ResponsePayload
public FFSampleResponse getResponseForSample(#RequestPayload FFSampleRequest request){
List<FFSampleResponse> ffSampleResponse = new ArrayList<FFSampleResponse>();
return ffSampleResponse ;
}
}
Soap request is as below:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://tempuri.org/">
<soapenv:Header/>
<soapenv:Body>
<ser:getResponseForSample>
<!--Optional:-->
<arg0>
<ser:Timestamp>"100"</ser:Timestamp>
<ser:SampleType>15</ser:SampleType>
</arg0>
</ser:getResponseForSample>
</soapenv:Body>
</soapenv:Envelope>
I receive the below exception as below:
19:20:34.472 WARN [tomcat-http--15][org.springframework.integration.ws.MarshallingWebServiceInboundGateway] failure occurred in gateway sendAndReceive
org.springframework.messaging.MessageHandlingException: org.springframework.expression.spel.SpelEvaluationException: EL1004E:(pos 8): Method call: Method getResponseForSample(javax.xml.bind.JAXBElement) cannot be found on com.java.myChannel.serviceImpl.FFSampleImpl type
at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:78)
at org.springframework.integration.handler.ServiceActivatingHandler.handleRequestMessage(ServiceActivatingHandler.java:71)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:170)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:101)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:97)
at org.springframework.integration.channel.AbstractSubscribablmyChannel.doSend(AbstractSubscribablmyChannel.java:77)
at org.springframework.integration.channel.AbstractMessagmyChannel.send(AbstractMessagmyChannel.java:255)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:114)
at org.springframework.messaging.core.GenericMessagingTemplate.doSendAndReceive(GenericMessagingTemplate.java:154)
at org.springframework.messaging.core.GenericMessagingTemplate.doSendAndReceive(GenericMessagingTemplate.java:44)
at org.springframework.messaging.core.AbstractMessagingTemplate.sendAndReceive(AbstractMessagingTemplate.java:75)
at org.springframework.integration.gateway.MessagingGatewaySupport.doSendAndReceive(MessagingGatewaySupport.java:250)
at org.springframework.integration.gateway.MessagingGatewaySupport.sendAndReceiveMessage(MessagingGatewaySupport.java:224)
at org.springframework.integration.ws.MarshallingWebServiceInboundGateway.doInvoke(MarshallingWebServiceInboundGateway.java:105)
at org.springframework.integration.ws.AbstractWebServiceInboundGateway.invoke(AbstractWebServiceInboundGateway.java:54)
at org.springframework.ws.server.endpoint.adapter.MessageEndpointAdapter.invoke(MessageEndpointAdapter.java:41)
at org.springframework.ws.server.MessageDispatcher.dispatch(MessageDispatcher.java:233)
at org.springframework.ws.server.MessageDispatcher.receive(MessageDispatcher.java:173)
at org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:88)
at org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle(WebServiceMessageReceiverHandlerAdapter.java:59)
at org.springframework.ws.transport.http.MessageDispatcherServlet.doService(MessageDispatcherServlet.java:292)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:409)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1044)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
I changed the service method parameter below but when I try to receive the values given in the SOAP message is not binding and I am getting null values for the "SampleType" and "Timestamp".
public class FFSampleImpl implements FFSampleInterface
{
#Autowired
FFSampleRequest request;
public FFSampleImpl() {
}
public FFSampleImpl(FFSampleRequest request) {
super();
this.request = request;
}
#Override
#PayloadRoot(localPart = "getResponseForSample" , namespace = "http://tempuri.org/")
#ResponsePayload
public FFSampleResponse getResponseForSample(#RequestPayload JAXBElement<FFSampleRequest> ffreq){
FFSampleResponse ffSampleResponse2 = new FFSampleResponse();
return ffSampleResponse2;
/* return FFSampleResponse;*/
}
I am suspecting that the entries that created in the object factory is not correct because feels that I am facing the challenge due to these entries only(below entries). Please let me know if we have any limitations to use jAXBMARSHALLER and xjc compiler also with the spring integration for marshalling/unmarshalling purpose, because I am facing lot of challenges with the soap mapping from xjc generated java objects. Please provide your inpur and let me know where I am missing for the binding of my soap parameters.
/**
* Create an instance of {#link JAXBElement }{#code <}{#link GetResponseForSample }{#code >}}
*
*/
#XmlElementDecl(namespace = "http://tempuri.org/", name = "getResponseForSample")
public JAXBElement<FFSampleRequest> createGetResponseForSample(FFSampleRequest value) {
return new JAXBElement<FFSampleRequest>(_GetResponseForSample_QNAME, FFSampleRequest.class, null, value);
}
/**
* Create an instance of {#link JAXBElement }{#code <}{#link GetResponseForSampleResponse }{#code >}}
*
*/
#XmlElementDecl(namespace = "http://tempuri.org/", name = "getResponseForSampleResponse")
public JAXBElement<GetResponseForSampleResponse> createGetResponseForSampleResponse(GetResponseForSampleResponse value) {
return new JAXBElement<GetResponseForSampleResponse>(_GetResponseForSampleResponse_QNAME, GetResponseForSampleResponse.class, null, value);
}
I have never used JAXBElement, however it might be usefull.
I generate classes for XSD and mark them as JaxB elements, e.g.:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "feeRequest", propOrder = {
"billerId",
"amount"
})
#XmlRootElement(name = "feeRequest")
public class FeeReqData {
#XmlElement(required = true)
protected Long billerId;
#XmlElement(required = true)
protected Double amount;
}
And use ObjectFactory only manually and don't rely on that JaxB magic.
This is enough for me to get correct model object based on inbound XML.
From other side, if you use FFSampleImpl as a Spring Integration <service-activator> there is no reason to mark its methods with Spring-WS annotations.

test if JAXB generated field is required

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;
}

element name and complex type name are same

I have an xsd, in which each element name and its complex type name are same. This creates problem when I am generating the Java from xsd using JAXB. This xsd has been provided by external service provider. Is the XSD wrong or I am doing something wrong in generating the xsd. Following is the sample of the xsd:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified" elementFormDefault="unqualified">
<xs:element name="Discounts" type="Discounts"/>
<xs:complexType name="Discounts">
<xs:complexContent>
<xs:extension base="listWithSizeAttribute">
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="Discounttest" type="Discounttest"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
Whenever I generate the Java object using JAXB, i get the error which says duplicate element Discounts. The entire xsd is full of such elements. Hence, I think something is wrong at my side.
Thanks,
Akshay
XML Schema
There isn't a problem when a global element and a complex type have the same name. I have flushed out your XML schema to make it valid so that I can run a schema compile on it.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
attributeFormDefault="unqualified" elementFormDefault="unqualified">
<xs:element name="Discounts" type="Discounts" />
<xs:complexType name="listWithSizeAttribute" />
<xs:complexType name="Discounttest" />
<xs:complexType name="Discounts">
<xs:complexContent>
<xs:extension base="listWithSizeAttribute">
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0"
name="Discounttest" type="Discounttest" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
Discounts
A JAXB (JSR-222) implementation generates Java classes for complex types, so you a class will be generated for the Discounts complex type.
package forum11981855;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Discounts", propOrder = {
"discounttest"
})
public class Discounts
extends ListWithSizeAttribute
{
#XmlElement(name = "Discounttest")
protected List<Discounttest> discounttest;
public List<Discounttest> getDiscounttest() {
if (discounttest == null) {
discounttest = new ArrayList<Discounttest>();
}
return this.discounttest;
}
}
ObjectFactory
An #XmlElementDecl annotation will be generated on the ObjectFactory class corresponding to the global element Discounts.
package forum11981855;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
private final static QName _Discounts_QNAME = new QName("", "Discounts");
public ObjectFactory() {
}
public Discounttest createDiscounttest() {
return new Discounttest();
}
public Discounts createDiscounts() {
return new Discounts();
}
public ListWithSizeAttribute createListWithSizeAttribute() {
return new ListWithSizeAttribute();
}
#XmlElementDecl(namespace = "", name = "Discounts")
public JAXBElement<Discounts> createDiscounts(Discounts value) {
return new JAXBElement<Discounts>(_Discounts_QNAME, Discounts.class, null, value);
}
}
For More Information
http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html

Cast JAXB2 generated object to JAXBElement?

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>

Resources