Crysis
Crysis

Reputation: 13

XML date validation using XSD assertion with Saxon.Api (.NET version)

The validation rule: The EndDate should be greater than the StartDate when EndDate is not null/empty. I'm getting an error on the last entry of employer while I'm expecting none. Am I doing something wrong?

XML

<Employers>
    <Employer>
      <StartDate>2020-01-01</StartDate>
      <EndDate></EndDate>
  </Employer>
  <Employer>
      <StartDate>2020-01-01</StartDate>
      <EndDate>2021-01-01</EndDate>
  </Employer>
</Employers>

XSD

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
   <xs:element name="Employers">
      <xs:complexType>
         <xs:sequence>
            <xs:element name="Employer" maxOccurs="unbounded">
               <xs:complexType>
                  <xs:sequence>
                        <xs:element name="StartDate" type="xs:date"></xs:element>
                            <xs:element name="EndDate" minOccurs="0">
                                <xs:simpleType>
                                     <xs:union>
                                    <xs:simpleType>
                                        <xs:restriction base="xs:string">
                                            <xs:minLength value="0" />
                                            <xs:maxLength value="0" />
                                        </xs:restriction>
                                    </xs:simpleType>
                                    <xs:simpleType>
                                        <xs:restriction base="xs:date">
                                        </xs:restriction>
                                    </xs:simpleType>
                                </xs:union>
                                </xs:simpleType>
                            </xs:element>
                        </xs:sequence>
                        <xs:assert test="if (./EndDate eq '') then true() else ./EndDate gt ./StartDate" xmlns:saxon="http://saxon.sf.net/" saxon:message="Error: errors"/>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema> 

Upvotes: 1

Views: 85

Answers (2)

Michael Kay
Michael Kay

Reputation: 163282

My preference when defining an element that can be either a date or empty is to use a list type with minLength=0, maxLength=1, rather than using a union of xs:date and zero-length string. I think this better reflects the semantics that the "empty" case represents "no date" rather than being something that exists and has a type other than date. The typed value is then always xs:date?, which plays better into the semantics of XPath comparison operators: the empty case is now an empty sequence, not a string. If you make EndDate a list type in this way, then the comparison becomes

empty(EndDate) or EndDate gt StartDate

or if you prefer,

not(EndDate le StartDate)

Upvotes: 1

zx485
zx485

Reputation: 29022

Make one minor correction:
Because your EndDate type is a union of xs:string and xs:date the type could be one or the other and it would fail if the type of the comparison does not match - yielding the error. This error can be fixed by casting it to a common type - here I chose xs:string. Now it checks the xs:string version of your date against the empty string and not the xs:date itself.

So change your assertion to

<xs:assert test="if (xs:string(EndDate) eq '') then true() else EndDate gt StartDate" xmlns:saxon="http://saxon.sf.net/" saxon:message="Error: errors" />

Now it should work as desired.

Upvotes: 1

Related Questions