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
}
Related
My company has subscribed to a service offered by a travel reservation system whereby they call us back when any changes occur to a travel itinerary. My task is to stand up a SOAP endpoint to accept this callback. I am doing this with standard jax-ws stuff.
Here is the essentials of their soap message:
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:eb="http://www.ebxml.org/namespaces/messageHeader" xmlns:swse="http://wse.sabre.com/eventing"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wse="http://schemas.xmlsoap.org/ws/2004/08/eventing">
<soap-env:Header>
<eb:MessageHeader eb:version="1.0"
soap-env:mustUnderstand="1">
<wse:MySubscription>7TZA</wse:MySubscription>
<swse:EventTopic>WSE.QUEUE.CCC.PNRCHNG</swse:EventTopic>
</eb:MessageHeader>
<wsa:Action>http://wse.sabre.com/EventSource/notification</wsa:Action>
<wsa:MessageID>721d8dc0-1307-4b27-a48b-a9ba7f7818c7</wsa:MessageID>
<wse:Identifer>7f9aad13-a8cd-4057-8c91-89ccfad64598</wse:Identifer>
<wsa:To>http://localhost:18888/</wsa:To>
</soap-env:Header>
<soap-env:Body>
<swse:CCC.PNRCHNG>
<swse:OWNPCC>N0V3</swse:OWNPCC>
<swse:HOMEPCC>W0H3</swse:HOMEPCC>
<swse:Locator>IGLYIZ</swse:Locator>
<swse:EventTimeStamp format="yyyy-MM-dd hh:mm:ss.fffffffff">2007-10-30 11:41:32.000986</swse:EventTimeStamp>
<swse:ChangeIndicators>
<swse:Indicator name="Ticketing">
<swse:hasChanged>Y</swse:hasChanged>
</swse:Indicator>
...
</swse:CCC.PNRCHNG>
</soap-env:Body>
I have a functioning SOAPHandler with a getHeaders() implementation wich is satisfying the mustUnderstand=1 requirement.
I have a #WebMethod that is successfully accepting a few of the top level parts of the payload. This is actually good enough for now, but I would like to understand how to write a #Webmethod that would accept the whole payload as a complex object.
I have a jibx-generated CCC.PNRCHNG class. But how would I write the #WebMethod to accept it? Below is the method that accepts the top level bits as separate #WebParams (Locator is all I really need right now)
#WebMethod(operationName="CCC.PNRCHNG", action="http://wse.sabre.com/EventSource/notification")
public Object onPnrEvent(
#WebParam(name="OWNPCC", targetNamespace=NS) String ownPcc,
#WebParam(name="HOMEPCC", targetNamespace=NS) String homePcc,
#WebParam(name="Locator", targetNamespace=NS) String locator
) {
try {
s_logger.info(locator);
}
catch(Exception e) {
s_logger.error(e);
}
return null;
}
It would be nice to get the full model so any advice there would be very much appreciated.
Best way would be to generate PortType based on WSDL with wsimport. If you don't have a WSDL, I wouldn't bother with writing a wrapped JAX-WS service.
On your class, add this annotation
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
Then write methods that accept the root element. Something like this:
#WebMethod(operationName="CCC.PNRCHNG", action="http://wse.sabre.com/EventSource/notification")
public Object onPnrEvent( #WebParam(name="CCC.PNRCHNG", targetNamespace=NS) PNRCHNG request) {
}
I am using the Jersey 2.1 API to return lists of JAXB annotationed objects.
I have a class Person
#XmlRootElement(name = "person")
public class Person { ...
In the Jersey API, when I return a List of Person and have the output set to xml, it creates a wrapper called <People> around my list:
<People>
<Person>
.. fields
</Person>
</People>
when I set the output to JSON format it does not add this extra People wrapper and I would like it to. I am using EclipseLink Moxy as the JSON provider. Is there a way to get the JSON output to look the same as the XML?
I came across a field for the Jersey 1.X API called FEATURE_XMLROOTELEMENT_PROCESSING that is supposed to enable this, but I don't know how to set this in 2.x. And the fact that it is doing it for XML output seems to indicate that it is already set. I just need to get the JSON to be the same!
Any help would be appreciated, thanks!
You could do the following:
Java Model
You could introduce a new class called People into your object model.
People
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="People")
#XmlAccessorType(XmlAccessType.FIELD)
public class People {
#XmlElementRef
private List<Person> person;
}
Person
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="Person")
public class Person {
}
RESTful Service
Instead of:
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Person> read() {
You would do:
#GET
#Produces(MediaType.APPLICATION_JSON)
public People read() {
By default MOXy won't included the root element. When working with Jersey you can leverage the MoxyJsonConfig object (see: http://blog.bdoughan.com/2013/06/moxy-is-new-default-json-binding.html).
import javax.ws.rs.ext.*;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
import org.glassfish.jersey.moxy.json.MoxyJsonConfig;
#Provider
public class MOXyJsonContextResolver implements ContextResolver<MoxyJsonConfig> {
private final MoxyJsonConfig config;
public MOXyJsonContextResolver() {
config = new MoxyJsonConfig()
.setIncludeRoot(true);
}
#Override
public MoxyJsonConfig getContext(Class<?> objectType) {
return config;
}
}
You can also leverage MOXy's MOXyJsonProvider class to do the same configuration:
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html
I'm developing a Java application with lots of complex Hibernate criteria queries. I would like to test these criteria to make sure they are selecting the right, and only the right, objects. One approach to this, of course, is to set up an in-memory database (e.g. HSQL) and, in each test, make a round trip to that database using the criteria and then assert that the query results match my expectations.
But I'm looking for a simpler solution, since Hibernate criteria are just a special kind of logical predicates about Java objects. Thus they could, in theory, be tested without accessing any database at all. For example, assuming that there is a entity called Cat:
class Cat {
Cat(String name, Integer age){
this.name = name;
this.age = age;
}
...
}
I would like to do something like this, to create criteria queries:
InMemoryCriteria criteria = InMemoryCriteria.forClass(Cat.class)
.add(Restrictions.like("name", "Fritz%"))
.add(Restrictions.or(
Restrictions.eq("age", new Integer(0)),
Restrictions.isNull("age")))
assertTrue(criteria.apply(new Cat("Foo", 0)))
assertTrue(criteria.apply(new Cat("Fritz Lang", 12)))
assertFalse(criteria.apply(new Cat("Foo", 12)))
The criteria could be used in production code like this:
criteria.getExecutableCriteria(session); //similar to DetachedCriteria
Is there any Java library that makes this kind of test possible?
You could use a mocking framework like Mockito to mock all relevant Hibernate classes and define expected behavior of these mocks.
Sounds like a lot of code, but since the Hibernate Criteria API is a fluent interface, all methods of Criteria return a new instance Criteria. So defining the mock behavior which is common to all tests is simple.
Here is an example using Mockito
#Mock
private SessionFactory sessionFactory;
#Mock
Session session;
#Mock
Criteria criteria;
CatDao serviceUnderTest;
#Before
public void before()
{
reset(sessionFactory, session, criteria);
when(sessionFactory.getCurrentSession()).thenReturn(session);
when(session.createCriteria(Cat.class)).thenReturn(criteria);
when(criteria.setFetchMode(anyString(), (FetchMode) anyObject())).thenReturn(criteria);
when(criteria.setFirstResult(anyInt())).thenReturn(criteria);
when(criteria.setMaxResults(anyInt())).thenReturn(criteria);
when(criteria.createAlias(anyString(), anyString())).thenReturn(criteria);
when(criteria.add((Criterion) anyObject())).thenReturn(criteria);
serviceUnderTest = new CatDao(sessionFactory);
}
All methods of the Criteria mock return the mock again.
In a concrete test you would then use a ArgumentCaptor and verify statements to investigate what happened to the mocked Criteria.
#Test
public void testGetCatByName()
{
ArgumentCaptor<Criterion> captor = ArgumentCaptor.forClass(Criterion.class);
serviceUnderTest.getCatByName("Tom");
// ensure a call to criteria.add and record the argument the method call had
verify(criteria).add(captor.capture());
Criterion criterion = captor.getValue();
Criterion expectation = Restrictions.eq("name", "Tom");
// toString() because two instances seem never two be equal
assertEquals(expectation.toString(), criterion.toString());
}
The problem I see with this kind of unitests is that they impose a lot of expectations about the class under test. If you think of serviceUnderTest
as a blackbox, you can't know how it retrieves the cat object by name. It could also use a LIKE criterion or even 'IN' instead of =, further it could use the
Example criterion. Or it could execute a native SQL query.
I think, you must do an integration test here with H2 or other in-memory database. As you said, if you only use mocks, you can see how object interacts with each other, but you never know what result list you get.
I am on the same page, not with Restriction or so, but with JPA 2.0 CriteriaQuery and CriteriaBuilder. I build complex predicates in my persistence layer, and at last, I find it becomes inevitable to test with data in db, as no one knows what would be the final query in SQL. And I decide that in this part of the system, an integration is needed, so I went for it.
At last it is not very hard to build such a test. You need H2 dependency, a persistence.xml like this:
<?xml version="1.0" encoding="UTF-8"?>
<!-- For H2 database integration tests. -->
<!-- For each int test, define unique name PU in this file and include SQL files in different paths. -->
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="test-item-history-service-bean" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider> <!-- mind here: must be this! cannot be JPA provider! -->
<class>com.data.company.Company</class>
<class>com.data.company.ItemHistory</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.url"
value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=Oracle;INIT=RUNSCRIPT FROM 'src/test/resources/db/item-history/create.sql'\;RUNSCRIPT FROM 'src/test/resources/db/item-history/populate.sql'"/>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.id.new_generator_mappings" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/> <!-- mind here! Can only be "update"! "create-drop" will prevent data insertion! -->
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.default_schema" value="main"/>
</properties>
</persistence-unit>
</persistence>
(Mind very carefully the comment in the XML above, it took me a week to finally solve them)
Note about the provider: see here: How to configure JPA for testing in Maven
And in the two sql files, you CREATE TABLE ... and INSERT INTO .... Insert whatever you like, as the data is part of the test.
And, a test like this:
/**
* Integration tests with in-memory H2 DB. Created because:
* - In-memory DB are relatively cheap to create and destroy, so these tests are quick
* - When using {#link javax.persistence.criteria.CriteriaQuery}, we inevitably introduce complex perdicates'
* construction into persistence layer, which is a drawback of it, but we cannot trade it with repetitive queries
* per id, which is a performance issue, so we need to find a way to test it
* - JBehave tests are for the user story flows, here we only want to check with the complex queries, certain
* records are returned; performance can be verified in UAM.
*/
#RunWith(MockitoJUnitRunner.class)
public class ItemHistoryPersistenceServiceBeanDBIntegrationTest {
private static EntityManagerFactory factory;
private EntityManager realEntityManager;
private ItemHistoryPersistenceServiceBean serviceBean;
private Query<String> inputQuery;
#BeforeClass
public static void prepare() {
factory = Persistence.createEntityManagerFactory("test-item-history-service-bean");
}
#Before
public void setup() {
realEntityManager = factory.createEntityManager();
EntityManager spy = spy(realEntityManager);
serviceBean = new ItemHistoryPersistenceServiceBean();
try {
// inject the real entity manager, instead of using mocks
Field entityManagerField = serviceBean.getClass().getDeclaredField("entityManager");
entityManagerField.setAccessible(true);
entityManagerField.set(serviceBean, spy);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new AssertionError("should not reach here");
}
inputQuery = new Query<>();
inputQuery.setObjectId("itemId");
}
#After
public void teardown() {
realEntityManager.close();
}
#Test
public void findByIdAndToken_shouldReturnRecordsMatchingOnlyTokenFilter() {
try {
// when
List<ItemHistory> actual = serviceBean.findByIdAndToken(inputQuery);
// then
assertEquals(2, actual.size());
assertThat(actual.get(0).getItemPackageName(), anyOf(is("orgId 3.88"), is("orgId 3.99.3")));
assertThat(actual.get(1).getItemPackageName(), anyOf(is("orgId 3.88"), is("orgId 3.99.3")));
} catch (DataLookupException e) {
throw new AssertionError("should not reach here");
}
}
}
I am working in a JAXWS/JAXB web service environment. JAXWS out of the box uses uses the JAXB to marshal/unmarshaler the web service payloads.
I also have a requirement to audit all request and response payloads.
I want a compact and concise marshaled representation of the payload for the audit (as a irrelevant side note - I am auditing using a java.util.concurrent.BlockingQueue and some consumer threads to put batches of audit data in the audit datasource).
I have binary content(mtom) included on some web service response payloads but I DO NOT want to marshal audit these because the serialized base64 would be too large.
So my need is to create a marshaller (exclusively for auditng) that in all cases will scrub binary content but then NOT scrub for the prime purpose of marshalling web service response payloads. I do XSD to Java xjc. I need to use the same XSD/JAXB namespace for both contexts/marshallers.
Java type converter:
<jxb:javaType name=""
parseMethod="com.xxx.xxx.ws.converter.XXXLongConverter.parseXXXLong"
printMethod="com.xxx.xxx.ws.converter.XXXLongConverter.printXXXLong" />
is will not work because 1. I would need to unregister the adapter http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/Marshaller.html#setAdapter%28java.lang.Class,%20A%29
for the marshaller and I don't THINK I have a hook into that for JAXWS. 2. I can't be guaranteed the class name that JAXB will decide to create in order to unregister it.
I created my own XMLAdapter and used the annox jaxb plugin
but that didn't really work for the same reasons the above didn't work.
Update: I now tried manually and reflectively walking through payload(to be audited) prior to marshalling to scrub the binary data but that got to be too much pain for what it was worth.
I should also mention that for brevity of the audit I am using jersey JSON serialization supporting JAXB
but I don't think that takes away or adds to my base problem:
How can I scrub data in one marshaller/unmarshaller but not another but both whose origin is the same JAXB context?
UPDATE: Never figured out an elegate way to do this. Not really possible at this point with the frameworks as they are. UPDATE: Not true. Extending AttachmentMarshaller (I like this a lot and will use it) or creating a "need-aware" XmlAdapter would work for the audit specific marshaller as #Blaise answers below.
UPDATE: If I may take this a step further to round out my use case...I mentioned above that for brevity of the audit I'd like to do Json Serialization of the JSONJAXBContext using jersey apis, specifically using the JSONMarshaller but the interface does not define setAdapter and setAttachmentMarshaller. Coming out of JSONJAXBContext.createJSONMarshaller() is a JSONMarshallerImpl implementation which do define these this methods. I will grudgingly cast to impl so I can set my custom attachment marshaller.
How can I scrub data in one marshaller/unmarshaller but not another
but both whose origin is the same JAXB context?
You could set your own implementation of AttachemntMarshaller and set it on the Marshaller that you are using for auditing.
Root
Below is a sample domain object with a byte[] property that by default will be represented as an element of type base64Binary.
package forum8914008;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
byte[] bytes;
}
Demo
The demo code below first marshals the object to XML, and then marshals it a second time with a custom impplementation of AttachmentMarshaller set.
package forum8914008;
import javax.activation.DataHandler;
import javax.xml.bind.*;
import javax.xml.bind.attachment.AttachmentMarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
root.bytes = "Hello World".getBytes();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
marshaller.setAttachmentMarshaller(new AttachmentMarshaller() {
#Override
public boolean isXOPPackage() {
return true;
}
#Override
public String addMtomAttachment(DataHandler arg0, String arg1,
String arg2) {
return "fake";
}
#Override
public String addMtomAttachment(byte[] arg0, int arg1, int arg2,
String arg3, String arg4, String arg5) {
return "fake";
}
#Override
public String addSwaRefAttachment(DataHandler arg0) {
return "fake";
}
});
marshaller.marshal(root, System.out);
}
}
Output
Below is the output from running the demo code. The first XML document could grow to be quite large if the byte[] was big. The second XML document would stay the same size.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<bytes>SGVsbG8gV29ybGQ=</bytes>
</root>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<bytes>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="fake"/>
</bytes>
</root>
How can I scrub data in one marshaller/unmarshaller but not another
but both whose origin is the same JAXB context?
You could support this use case with an XmlAdapter.
XmlAdapter (ByteArrayAdapter)
The following XmlAdapter is used to convert a byte[] to a byte[]. In its default state it will return the original byte[], it also has a audit state where it will return an empty byte[].
package forum8914008;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class ByteArrayAdapter extends XmlAdapter<byte[], byte[]> {
private boolean audit;
public ByteArrayAdapter() {
this(false);
}
public ByteArrayAdapter(boolean audit) {
this.audit = audit;
}
#Override
public byte[] marshal(byte[] bytes) throws Exception {
if(audit) {
return new byte[0];
}
return bytes;
}
#Override
public byte[] unmarshal(byte[] bytes) throws Exception {
return bytes;
}
}
package-info
The #XmlJavaTypeAdapter annotation is used tp register the XmlAdapter. When used at the package level it will apply to all properties of the specified type in that package (see: http://blog.bdoughan.com/2012/02/jaxb-and-package-level-xmladapters.html).
#XmlJavaTypeAdapter(value=ByteArrayAdapter.class, type=byte[].class)
package forum8914008;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
Root
Below is a sample domain object with a byte[] property that by default will be represented as an element of type base64Binary.
package forum8914008;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
byte[] bytes;
}
Demo
The demo code below first marshals the object with the default state of the ByteArrayAdapter which will return the real byte[] and the marshals the object a second time with a stateful ByteArrayAdapter set which will convert all byte[] values to an empty byte[].
package forum8914008;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
root.bytes = "Hello World".getBytes();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
marshaller.setAdapter(new ByteArrayAdapter(true));
marshaller.marshal(root, System.out);
}
}
Output
Below is the output from running the demo code. The XmlAdapter would apply to all mapped fields/properties of type byte[].
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<bytes>SGVsbG8gV29ybGQ=</bytes>
</root>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<bytes></bytes>
</root>
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