tags:

views:

165

answers:

2

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?

+1  A: 

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"/&gt;

    <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"&gt;
    <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 (http://stackoverflow.com/questions/3568540/cast-jaxb2-generated-object-to-jaxbelement/3620867#3620867)

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" /> 
Blaise Doughan
is there a way to do #1 in the xsd? and for #2, it seem slike you have all 3 options the same...
wuntee
I have updated my answer to include an example.
Blaise Doughan
I have also posted an example to my blog where the message object has a property of type Object, and leverages the @XmlAnyElement annotation to sort out the payload: http://bdoughan.blogspot.com/2010/08/using-xmlanyelement-to-build-generic.html
Blaise Doughan
can you see my next answer... thanks for the help.
wuntee
I have updated my answer based on your second post. This is the method signature I get when I run those schemas through XJC: public void setRpcOperation(RpcOperationType value)
Blaise Doughan
yes, with that I get the same thing. instead of using what i pasted, use the full netconf.xsd from: http://www.iana.org/assignments/xml-registry/schema/netconf.xsd it will generate the function how i see it: public void setRpcOperation(JAXBElement<? extends RpcOperationType> value)
wuntee
Thanks for all of the help... now I am getting a '[com.sun.istack.SAXException2: unable to marshal type "netconf.RpcType" as an element because it is missing an @XmlRootElement annotation]' I am sure its the same issue as described: http://weblogs.java.net/blog/kohsuke/archive/2006/03/why_does_jaxb_p.html , but I am trying to modify the XSD as least as possible. Any ideas on how to get around that?
wuntee
Have you received enough information from http://stackoverflow.com/q/3639313/383861 to solve this issue?
Blaise Doughan
A: 

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>
wuntee
Is that a question or an answer?
skaffman
Question... the last line 'rpc.setRpcOperation();' needs something passed to it... Is it possible the the rpcOperation object to type JAXBElement<? Extends RpcOperationType> ?
wuntee