17

Documentation for required says:

If required() is true, then Javabean property is mapped to an XML schema element declaration with minOccurs="1". maxOccurs is "1" for a single valued property and "unbounded" for a multivalued property.

If required() is false, then the Javabean property is mapped to XML Schema element declaration with minOccurs="0". maxOccurs is "1" for a single valued property and "unbounded" for a multivalued property.

Documentation for nillable says:

If nillable() is true, then the JavaBean property is mapped to a XML Schema nillable element declaration.


Code for xs:complexType:
public class WSData {
    //...

    @XmlElement(required = true, nillable = false)
    public void setMonth(XmlMonthType month) {
        this.month = month;
    }

    public void setUserLogin(String userLogin) {
        this.userLogin = userLogin;
    }
}

Code for xs:simpleType:

@XmlType
@XmlEnum(Integer.class)
public enum XmlMonthType {
    @XmlEnumValue("1")
    JANUARY,
    @XmlEnumValue("2")
    FEBRUARY,
    @XmlEnumValue("3")
    MARCH,
    /* ... months 4 ~9 ... */
    @XmlEnumValue("10")
    OCTOBER,
    @XmlEnumValue("11")
    NOVEMBER,
    @XmlEnumValue("12")
    DECEMBER;
}

Generated XML Schema:

<xs:complexType name="wsData">
  <xs:sequence>
    <xs:element name="month" type="xs:string"/>
    <xs:element minOccurs="0" name="userLogin" type="xs:string"/>
  </xs:sequence>
</xs:complexType>

<xs:simpleType name="xmlMonthType">
  <xs:restriction base="xs:int">
    <xs:enumeration value="1"/>
    <xs:enumeration value="2"/>
    <xs:enumeration value="3"/>
    <!-- ... months 4 ~9 ... -->
    <xs:enumeration value="10"/>
    <xs:enumeration value="11"/>
    <xs:enumeration value="12"/>
  </xs:restriction>
</xs:simpleType>


The facts:
  1. The default value for minOccurs is 1. So, month is required (must exist);
  2. The month has a restriction. So, month can only have a value defined by one of the 12 defined enumerations;
  3. The default value for nillable is false. So, month can't have empty values;
  4. The XML Schema is generated correctly.

The problems:

  1. It is accepting month's abcense (mustn't exist);
  2. It is accepting any values for month, like 13 (except when isn't parseable to Integer);
  3. It is accepting empty values;

I wasn't expecting these problems, am I missing something?
If that behavior is correct, what is the purpose of required, nillable and xs:restriction?

falsarella
  • 12,217
  • 9
  • 69
  • 115

5 Answers5

12

Nillable allows empty values. For example, if you have an Integer or a Date, if it's nillable, the XML tag could be empty. If it's not nillable but not required, the XML element would either have to exist with a valid content, or not exist at all; an empty tag wouldn't be valid.

bcody
  • 2,489
  • 1
  • 21
  • 21
  • Do you mean that nillable is used to define if it can have empty values when the tag exists; and that required is used to define if the tag have to exist or not? In this case, why is it allowing empty tags and why it isn't requiring to have that tag at all? How do I annotate to make minOccurs 1? – falsarella Feb 02 '12 at 13:40
  • minOccurs is 1 by default. Your original schema says that month is mandatory (tag exists and not empty). – bcody Feb 02 '12 at 14:32
  • Then why it accepts month's abcense or empty value? It does not generate an error at the response! – falsarella Feb 02 '12 at 15:00
  • Hmm, are you sure the XML is actually being validated against the schema? – bcody Feb 02 '12 at 15:21
5

Make minOccurs 1, to make month required;

The default value for minOccurs is one, so the month element is required. Note how minOccurs="0" had to be added to userLogin to make it optional.

<xs:complexType name="wsData">
  <xs:sequence>
    <xs:element name="month" type="xs:string"/>
    <xs:element minOccurs="0" name="userLogin" type="xs:string"/>
  </xs:sequence>
</xs:complexType>

Validate month with its generated restriction (without a XmlAdapter).

You can set an instance of Schema on the Unmarshaller to have the input validated:

Demo

The following code can be used to generate the XML schema:

package forum9111936;

import java.io.IOException;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.SchemaOutputResolver;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(WSData.class);
        SchemaOutputResolver sor = new SchemaOutputResolver() {

            @Override
            public Result createOutput(String namespaceUri,
                    String suggestedFileName) throws IOException {
                StreamResult result = new StreamResult(System.out);
                result.setSystemId(suggestedFileName);
                return result;
            }

        };
        jc.generateSchema(sor);
        System.out.println();
    }

}

UPDATE

The JAXB RI normally throws a ValidationEvent of severity 1 for conversion issues. The default ValidationEventHandler ignores all issues of severity less than 2. This normally results in the value being set to null. You can override the ValidationEventHandler as follows:

    unmarshaller.setEventHandler(new ValidationEventHandler() {
        @Override
        public boolean handleEvent(ValidationEvent event) {
            System.out.println(event);
            return event.getSeverity() < ValidationEvent.ERROR;
        }
    });

However the JAXB RI does not appear to throw events related to converting enum values (possible bug). If you happen to be using EclipseLink JAXB (MOXy) as your JAXB provider then you will get an exception like:

Exception in thread "main" Local Exception Stack: 
Exception [EclipseLink-116] (Eclipse Persistence Services - 2.4.0.qualifier): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: No conversion value provided for the value [13] in field [month/text()].
Mapping: org.eclipse.persistence.oxm.mappings.XMLDirectMapping[month-->month/text()]
Descriptor: XMLDescriptor(forum9111936.WSData --> [])
    at org.eclipse.persistence.exceptions.DescriptorException.noFieldValueConversionToAttributeValueProvided(DescriptorException.java:1052)
    at org.eclipse.persistence.mappings.converters.ObjectTypeConverter.convertDataValueToObjectValue(ObjectTypeConverter.java:140)
    at org.eclipse.persistence.oxm.mappings.XMLDirectMapping.getAttributeValue(XMLDirectMapping.java:287)
    at org.eclipse.persistence.internal.oxm.XMLDirectMappingNodeValue.endElement(XMLDirectMappingNodeValue.java:190)
    at org.eclipse.persistence.oxm.record.UnmarshalRecord.endElement(UnmarshalRecord.java:910)
    at org.eclipse.persistence.internal.oxm.record.XMLStreamReaderReader.parseEvent(XMLStreamReaderReader.java:133)
    at org.eclipse.persistence.internal.oxm.record.XMLStreamReaderReader.parse(XMLStreamReaderReader.java:83)
    at org.eclipse.persistence.internal.oxm.record.XMLStreamReaderReader.parse(XMLStreamReaderReader.java:72)
    at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:838)
    at org.eclipse.persistence.oxm.XMLUnmarshaller.unmarshal(XMLUnmarshaller.java:626)
    at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:472)
    at forum9111936.Demo2.main(Demo2.java:30)

For More Information

bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • If month is required, then why does it accepts null or month's absence (0 ocurrences)? – falsarella Feb 02 '12 at 16:06
  • About how to generate the schema.. What I know about the generation is that it is automatic here.. On JBoss startup something creates it given the web.xml mapping and WebService's and XmlXXX annotations and that's all. BTW, I guess that's not the point, my xsd is being generated properly. But it accepts 13 as an input for the month (unmarshalled to null), for example. Also, it accepts null and it's absence. Why? Creating an enumerations restriction and not creating will give me the same behavior of acceptance. – falsarella Feb 02 '12 at 16:40
  • I found that I can create an afterUnmarshal method to handle null's, for example, and it works, but what's the purpose of putting something with minOccurs 1 in xsd if it accepts 0 ocurrences and what's the purpose of creating a restriction (with enum) if it is not validated if the input value is in that 'range'? – falsarella Feb 02 '12 at 16:42
  • 1
    @falsarella - I have updated my answer to address your comments. In the JAXB (JSR-222) expert group we decided to make JAXB lenient in the processing of XML (this is what the majority of users want). For those developers that want to ensure the XML strictly follows the spec JAXB leverages the `javax.xml.validation` APIs. In this way I feel the JAXB offers the best of both worlds. The `ValidationEventHandler` mechanism offers even more control, although it appears you may have encountered a bug in the JAXB RI. I hope this helps. – bdoughan Feb 03 '12 at 19:54
  • thank you! Also, I will open a bug for the enum Validation (it appears that JAXB saves the enumerations in a map and when it tries to find a value out-of-enumaration, it returns null without bearing an exception, but it would be better to emulate the enum valueOf behavior in these cases)! – falsarella Feb 06 '12 at 10:56
4

The purposes of required and minOccurs aren't mislead, the problem is that the schema validation is not enabled. Just enable SchemaValidation to the WebService and define the order of the XmlType's mapping as follows:

Web Service:

@javax.jws.WebService
@org.jboss.ws.annotation.SchemaValidation(enabled = true)
public class WebServiceClass {

    @javax.jws.WebMethod
    public WSResponseData webServiceMethod() {
        //...
    }
}

XmlType:

@javax.xml.bind.annotation.XmlType(propOrder = {"field1", "field2", "field3"})
public class WSData {
    //...

    private String field1;

    private Long field2;

    private XmlMonthType field3;

    //...
}
falsarella
  • 12,217
  • 9
  • 69
  • 115
1

Indeed it seems that SOAP envelopes aren't validated upon WSDL, neither on server or client.

And I think it's best this way. Validation consumes resources and time. For most WebServices, all we need with WSDL is to provide to client the definitions to be able to talk to WebService, and not set restrictions like if a property can or not be null.

When server and client are on same PC, Axis2 add 3-5 milisecs of overhead compared to internal calls. Unecessary validation would only increase overhead. I's better to make operations throw an exception and manually validate what's really needed.

Hikari
  • 3,797
  • 12
  • 47
  • 77
1

I looked into the XSD structure that you have posted - and find that you have not used complex type that you have defined for the month , Insteqad of handling it on java code you can mention that all in XSD , and also the minOccur on month - element in your complex type is missing . Please use any standered XSD to java generator , it will do all the java file generation task for you

The convention are as follow - 

MinOccur = 0 --> the element can be abscent in input , and can be present

MinOccur = 1 --> the element must be there in input
(but if you use it, then your java generated member will be of list type - list of ENUM for Int )

If you don't write MinOccur in attributes - then it makes the element mandatory , ( then you java generated member will be of simply ENUM for int )


MaxOccur = 1 --> minimum one element can be there in input 
(but if you use it, then your java generated member will be of list type - list of ENUM for Int )


MaxOccur = unbound --> only one element can be there in input 
(if you use it, then your java generated member will be of list type - list of ENUM for Int )

   <xs:complexType name="wsData">
  <xs:sequence>
    <xs:element name="month" type="xmlMonthType" minOccurs="1" nillable="false" />
    <xs:element name="userLogin" type="xs:string" minOccurs="0" />
  </xs:sequence>
</xs:complexType>

<xs:simpleType name="xmlMonthType">
  <xs:restriction base="xs:int">
    <xs:enumeration value="1"/>
    <xs:enumeration value="2"/>
    <xs:enumeration value="3"/>
    <!-- ... months 4 ~9 ... -->
    <xs:enumeration value="10"/>
    <xs:enumeration value="11"/>
    <xs:enumeration value="12"/>
  </xs:restriction>
</xs:simpleType>
Ashish Shetkar
  • 1,414
  • 2
  • 18
  • 35