I am building a web service using the Zend Framework. I am using the Zend_Soap_AutoDiscover class for the generation of my WSDL. I am using various complex type in this web service form example:
StockItemEntity Class
class StockItemEntity {
/** #var string */
public $sStockCode;
/** #var string */
public $sQty;
public function __construct($sStockCode, $sQty){
$this->sStockCode = $sStockCode;
$this->sQty = $sQty;
}
}
WSDL Definition
<xsd:complexType name="StockItemEntity">
<xsd:all>
<xsd:element name="sStockCode" type="xsd:string" nillable="true"/>
<xsd:element name="sQty" type="xsd:string" nillable="true"/>
</xsd:all>
</xsd:complexType>
From what I understood from reading over the web the nillable="true" is there because properties of any object can be set to null. Thus the nillable="true" is need to maintain a valid XML document even if the StockItemEntity object has all its properties set to null.
My concern is that those two properties must always be passed to the web method. Is it possible to remove the "nillable=true" to sort of force the properties not to be null? Or else is there any way to force non null values in those properties. I was hoping to avoid having to validate them on the webservice side.
Thanks
Kind Regards
Gabriel
At some stage between Zend Framework 1.10.7 and 1.11.0, they introduced a piece of code that uses reflection to check if there's a default value defined for an attribute of a class and, if not, it decides that the class is "nillable" and adds nillable="True" to the attribute definition in the WSDL. This is apparently meant to improve interoperability with some flaky version of .Net.
To stop Zend Framework from adding the nillable flag, just initialize those properties when they are declared, eg.
/** #var string */
public $sStockCode = '';
/** #var string */
public $sQty = '';
Hope that helps.
nillable='true' indicates that the element CAN be null in an xml document instance.
If the element in an xml document IS null, then this is indicated by
<sStockCode xsi:nil='true' />
(the element itself must be empty)
It sounds like you need to change the xsd to nillable='false'. Also, string can be empty - if you want to prevent against empty strings with the minLength attribute in your xsd. You can also use default to specify a default value for the string if it isn't provided.
W3schools has good reference material for XSD's here
HTH
Related
<xsd:element name="echodemo" type="EchodemoType">
<xsd:annotation>
<xsd:documentation>
Root element
</xsd:documentation>
</xsd:annotation>
this is my XSD.
but when i had generate the java object from xsd by chooing generate java in RSA ., it generating EchodemoType.java ..
so am creating instance LIKE below before passing the request to my service
EchodemoType request = new EchodemoType();
request.setEchoRequest("This is Echo String !!!");
request .setVersion(new BigDecimal("1.0"));
while invoke my webservice am seeing the the soap message like below.,
<EchodemoType version="1.0">
<ns2:echoRequest>This is Echo String !!!</ns2:echoRequest>
</EchodemoType>
basically i want the soap message start tag with element name "echodemo" rather with type "EchodemoType".also i want it with the name space
<echodemo version="1.0" xmlns="http://test.com/api/test/services/echodemo">
<ns2:echoRequest>This is Echo String !!!</ns2:echoRequest>here
</echodemo>
please tell me the way to generate the class to have that soap message or where i need to correct this?
Make use of Object Factory for mashelling the object like below you no need to have #XmlRootElement in DemoType.java .,
DemoType demoServiceRequest = new DemoType();
demoServiceRequest.setName("fgfg");
ObjectFactory obDemo = new ObjectFactory();
Request requestObject = new Request();
requestObject.setAny(obDemo.createDemo(demoServiceRequest));
And add DemoType class at Request.java like #XmlSeeAlso({DemoType.class})
I am struggling with gsoap's return parameter binding. I have a function which returns char** which is composed like this: {char*, ..., char*, NULL}. I want the generated web service to be able to transmit and process multiple strings in one parameter. Gsoap however generates only:
<element name="retVal" type="xsd:string" minOccurs="0" maxOccurs="1" nillable="true"/>
I would like it to return an array of strings, not a single string.
If I undesrstand your question
You can develop your own function which call the gSoap function to get the returned string from the gSoap function then split the gSoap sting to sub strings and put them into an array and then return your array of strings
I found an answer in the documentation. It turns out that I had to create a structure to be used to return the value and name the variables appropriately. In my case:
struct ns_cl_get_indexes_retVal {
int __sizeIndex;
char** index;
};
and the function in the WS-base header:
int ns__cl_get_indexes ( int32_t db_id, struct ns_cl_get_indexes_retVal& retVal );
Which resulted in a proper xml generation, as seen in wsdl:
<element name="index" type="xsd:string" minOccurs="0" maxOccurs="unbounded" nillable="true"/>
I have to expose an ejb service layer via jax-ws .
I have generated the web service using jax-ws and wsimport but I'm stopped by a strange things ; Date are being mapped to XmlGregorianCalendar .
Is it possible to use classic java Date instead ?
Can you show me the right way to proceed ?
Thanks .
Edit:
this the binding file i used :
thanks , I modified slightly your xml and attached it with netbeans to the client's webservice and it worked . This the binding I used :
<jaxws:bindings node="wsdl:definitions/wsdl:types/xsd:schema"
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" wsdlLocation="../wsdl/localhost_8080/web_test/Testor.wsdl" >
<jaxb:globalBindings>
<jaxb:javaType name="java.util.Date"
xmlType="xsd:dateTime"
parseMethod="lol.XsdDateTimeConverter.unmarshal"
printMethod="lol.XsdDateTimeConverter.marshalDateTime"
/><jaxb:javaType
name="java.util.Date"
xmlType="xsd:date"
parseMethod="lol.XsdDateTimeConverter.unmarshal"
printMethod="lol.XsdDateTimeConverter.marshalDate"
/>
</jaxb:globalBindings>
</jaxws:bindings>
Not tested, but should work. First create such class:
import javax.xml.bind.DatatypeConverter;
public class XsdDateTimeConverter {
public static Date unmarshal(String dateTime) {
return DatatypeConverter.parseDate(dateTime).getTime();
}
public static String marshalDate(Date date) {
final GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(date);
return DatatypeConverter.printDate(calendar);
}
public static String marshalDateTime(Date dateTime) {
final GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(dateTime);
return DatatypeConverter.printDateTime(calendar);
}
}
Then add this to custom xjb file:
<javaType
name="java.util.Date"
xmlType="xs:dateTime"
parseMethod="XsdDateTimeConverter.unmarshal"
printMethod="XsdDateTimeConverter.marshalDateTime"
/>
<javaType
name="java.util.Date"
xmlType="xs:date"
parseMethod="XsdDateTimeConverter.unmarshal"
printMethod="XsdDateTimeConverter.marshalDate"
/>
</globalBindings>
Not tested, but should work. Based on my answer here: JAX-WS and Joda-Time?
Thanks Tomasz. The above solution works.
But wsimport also adds its set of Adapters like Adapter1.java and Adapter2.java with its package org.w3._2001.xmlschema, which really doesnot match my own package structure.
I found a way to change this package name using another jaxb binding. Actually, I searched for this a lot and could not find this easily, so I am adding it here for anyone looking for the same.
Add the following binding in the wsimport using '-b binding.xml'. Note that wsimport can work with multiple binding files.
binding.xml content below:
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:version="2.0">
<annotation><appinfo>
<jaxb:schemaBindings>
<jaxb:package name="com.abc.xyz.utils"/>
</jaxb:schemaBindings>
</appinfo></annotation>
</schema>
Let's say we have the following Java 1.5 enumeration:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
#XmlAccessorType(XmlAccessType.FIELD)
public enum ReturnCode {
OK(0,"Ok"),
ERROR_VALIDATION(1,"Validation Error"),
ERROR_TRANSPORT(2, "Transport Error"),
ERROR_CASE_01(101, "Business situation #01"),
ERROR_CASE_02(102, "Business situation #02"),
ERROR_CASE_03(103, "Business situation #03");
#XmlElement(nillable=false, required=true)
private Integer code = 0;
#XmlElement(nillable=false, required=true)
private String message = null;
private ReturnCode(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}
I am using Apache CXF and the generated WSDL, as expected, translates the aforementioned enum into a restriction:
<xsd:simpleType name="ReturnCode">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="OK"/>
<xsd:enumeration value="ERROR_VALIDATION"/>
<xsd:enumeration value="ERROR_TRANSPORT"/>
<xsd:enumeration value="ERROR_CASE_01"/>
<xsd:enumeration value="ERROR_CASE_02"/>
<xsd:enumeration value="ERROR_CASE_03"/>
</xsd:restriction>
</xsd:simpleType>
So far so good and it is a desirable feature. I myself remember to be struggling with such structures before Apache CXF (back when I used XFire).
However, this is not the case here. I want to produce a different result. I want that the enum be translated into a Complex Type and that both attributes code and message are translated into XML elements when an object containing an instance of this enumeration is marshalled. I only want it to not behave like an enum. I know I could accomplish that if I used a plain class instead of an enum. However, I'd very much like to keep it an enum so I kept it type-safe in the java-part of the code.
If the generated WSDL could still have a restriction as to the possible values, it would be the perfect scenario. I could do without it, however. The main thing here would be to keep it a Java 1.5 enum while still marshalling (and generating a WSDL) ReturnCode as a Complex Type with code and message as its elements.
I tried to hint that with the given JAXWS Annotations placed in the enum source code. Is it somehow possible to accomplish that with just those (or some other) annotations? Or would I have to write a custom marshaller/unmarshaller and WSDL-generator?
Thank you very much!
Best regards,
Filipe Fedalto
Use enums in your java server code and translate to complex type in the interface of you service.
Example:
#WebMethod
public ComplexType GetInfo(){
ReturnCode response;
response = ReturnCode.OK;
ComplexType wsResponse;
wsResponse = response.toComplexType()
return wsResponse;
}
#WebMethod
public void PutInfo(ComplexType input){
ReturnCode request = ReturnCode.fromComplexType(input);
//more code
}
I've made Web Services with Delphi in the past but most were pretty simple that just took a few parameters and returned a single value to the client. A new service I am working on calls for me to be able to send and receive complex types. Consider the following types are defined in my code:
TBaseRequest = Class(TRemotable)
private
FUsername: string;
FPassword: string;
published
Property Username: String read FUsername write FUsername;
Property Password: String read FPassword write FPassword;
End;
TBaseResponse = Class(TRemotable)
private
FStatusMessage: string;
FStatusCode: integer;
published
Property StatusMessage: string read FStatusMessage write FStatusMessage;
Property StatusCode: integer read FStatusCode write FStatusCode;
End;
TSepecialRequest = class(TBaseRequest)
private
FExtraParam: string;
published
Property ExtraParam: String read FExtraParam write FExtraParam;
end;
TSpecialResponse = class(TBaseResponse)
private
FExtraResult: string;
published
Property ExtraResult: String read FExtraResultwrite FExtraResult;
end;
All of these classes are registerd with RemClassRegistry.RegisterXSClass.
Now I've also got the following function defined in the interface for this webservice:
function SpecialMethod(request:TSepecialRequest): TSpecialResponse;
In the service code I can easily access the parent class properties like Username and Password, but if we look at the WSDL that is generated we see that the TSpecialRequest and TSpecialResponse class members are included in the schema section.
<xs:complexType name="TSpecialRequest">
<xs:complexContent>
<xs:extension base="TBaseRequest">
<xs:sequence>
<xs:element name="ExtraParam" type="xs:string"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="TSpecialResponse">
<xs:complexContent>
<xs:extension base="TBaseResponse">
<xs:sequence>
<xs:element name="ExtraResult" type="xs:string"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
This schema fragment in the WSDL shows that the TSpecials are extentions of the TBase classes and all is well except that the decription of the TBase classes are not included in the schema. I would expect there to also be a section like this, but it is missing:
<xs:complexType name="TBaseRequest">
<xs:sequence>
<xs:element name="Username" type="xs:string"/>
<xs:element name="Password" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TBaseResponse">
<xs:sequence>
<xs:element name="StatusMessage" type="xs:string"/>
<xs:element name="StatusCode" type="xs:int"/>
</xs:sequence>
</xs:complexType>
However this schema fragment is missing from the generated WSDL. This means that any client attempting to use this service will not be able to correctly generate requests or interpret responses. For example, if I attempt to load the geneated WSDL into the WSDL importer in Delphi 2009, I get the following classes:
TSpecialRequest = class(TRemotable)
private
FExtraParam: WideString;
published
property ExtraParam: WideString read FExtraParam write FExtraParam;
end;
TSpecialResponse = class(TRemotable)
private
FStatusMessage: WideString;
FStatusCode: Integer;
published
property StatusMessag: WideString read FStatusMessage write FStatusMessage;
property StatusCode: Integer read FStatusCode write FStatusCode;
end;
The result is that the client code is unable to do things like set the username and password members that should be part of TSpecialRequest.
Does anyone have any clue why this is happening or what I can do about it?
I don't think I am going to get an answer but I have found a work around. I'm not really satisified with it, but It gets me passed the problem. The symptom described above can be avoided by using Object Composition instead of inheritance. This code functions as expected but it is less simple to use. I suppose I'll want to come up with some kind of message factory in my implementation but thats getting off topic.
This solution can be illustrated by the following code:
TBaseRequest = Class(TRemotable)
private
FUsername: string;
FPassword: string;
published
Property Username: String read FUsername write FUsername;
Property Password: String read FPassword write FPassword;
end;
TSepecialRequest = class(TRemotable)
private
FExtraParam: string;
FBaseRequest: TBaseRequest;
published
Property ExtraParam: String read FExtraParam write FExtraParam;
Property BaseRequest: TBaseRequest read FBaseRequest write FBaseRequest;
end;
Delphi only generates the schema for classes that are directly referenced in the web service's interface. Ideally it should include base classes back to TRemotable, but it doesn't.
I've found, though, if you add a dummy method to your interface with your base classes in the parameter list, Delphi will generate the missing definitions. For example:
procedure BaseClasses(BaseRequest: TBaseRequest; BaseResponse: TBaseResponse); stdcall;
This is definitely a hack and it's a bit annoying to have to expose a method like this, but it does accomplish what you want done.
I know this response is a bit late for you but maybe it'll useful for someone else down the road.