XSD - How to validate an attribute value of different strings with different custom validations? - validation

Greetings fellow stackoverflowians,
Work is asking for XML validation by using XSD and I'm the lucky one who was selected to spear head this effort (I have no XSD experience). I'm currently learning XSD so I apologize if I'm using wrong XSD lingo.
I was able to create a XSD to blankly validate the XML, but now custom validation for specific elements has been requested. I'm currently stuck on how to validate each string in the second attribute of element property that requires each string to be checked against different validation rules.
This is my first time asking a question, but have enjoyed the shared knowledge in stackoverflow for years. Any additional feedback on proper question asking is appreciate and I sincerely appreciate the help figuring out this problem.
I'm using version 1.0, but willing to bump up to version 1.1 if needed
Here is part of the XML that I have to validate with custom validation
<properties>
<property name="description" value="long reference string"></property>
<property name="passfailcriteria" value="long BDD string"></property>
<property name="automationpath" value="url"></property>
<property name="passfailcriteria" value="stuff, stuff, stuff"></property>
<property name="teststatus" value="ready"></property>
<property name="autocandidacy" value="automated"></property>
</properties>
Here is my current attempt at XSD validation
<xs:element name="properties">
<xs:complexType>
<xs:sequence>
<xs:element ref="property" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="property">
<xs:complexType>
<xs:attribute name="name" type="property-names" use="required"/>
<xs:attribute name="value" type="property-values" use="required"/>
</xs:complexType>
</xs:element>
<xs:simpleType name="property-names">
<xs:restriction base="xs:string">
<xs:enumeration value="autocandidacy"/>
<xs:enumeration value="automationpath"/>
<xs:enumeration value="description"/>
<xs:enumeration value="passfailcriteria"/>
<xs:enumeration value="ptes_req"/>
<xs:enumeration value="tags"/>
<xs:enumeration value="teststatus"/>
<xs:enumeration value="verificationmentod"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="property-values">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="autocandidacy" type="autocandidacy"/>
<xs:element name="automationpath" type="xs:string"/>
<xs:element name="description" type="xs:string"/>
<xs:element name="passfailcriteria" type="xs:string"/>
<xs:element name="ptes_req" type="xs:string"/>
<xs:element name="tags" type="tags"/>
<xs:element name="teststatus" type="teststatus"/>
<xs:element name="verificationmentod" type="verificationmentod"/>
</xs:sequence>
</xs:complexType>
<!-- Verification Mentod-->
<!-- List of values acceptable for the property Verification Method -->
<xs:simpleType name="verificationmentod">
<xs:restriction base="xs:string">
<xs:enumeration value="Demonstration"/>
<xs:enumeration value="Exam of Children"/>
<xs:enumeration value="Test"/>
<xs:enumeration value="Analysis"/>
</xs:restriction>
</xs:simpleType>
<!-- Auto Candidacy-->
<!-- List of values acceptable for the property Auto Candidacy -->
<xs:simpleType name="autocandidacy">
<xs:restriction base="xs:string">
<xs:enumeration value="AIP"/>
<xs:enumeration value="automated"/>
<xs:enumeration value="No"/>
<xs:enumeration value="Ready"/>
<xs:enumeration value="Yes"/>
</xs:restriction>
</xs:simpleType>
<!-- Test Status Value -->
<!-- List of values acceptable for the property teststatus -->
<xs:simpleType name="teststatus">
<xs:restriction base="xs:string">
<xs:enumeration value="WIP"/>
<xs:enumeration value="Ready for Review"/>
<xs:enumeration value="In Review"/>
<xs:enumeration value="Ready"/>
<xs:enumeration value="Repair"/>
</xs:restriction>
</xs:simpleType>
<!-- tags -->
<!-- Checks that the string is a comma seperated value -->
<xs:simpleType name="tags" >
<xs:restriction base="xs:string">
<xs:pattern value="[a-zA-Z][a-zA-Z0-9]*(\s*,\s*[a-zA-Z][a-zA-Z0-9]*){0,10}" />
</xs:restriction>
</xs:simpleType>
I broke each string that needs a custom validation into it's own block, put all required values into a block property-values and tried to reference that in the attribute that needs the validation <xs:attribute name="value" type="property-values" use="required"/>.
I can't seem to figure out how to validate the strings in attribute name="name" with the related attribute name="value" that uses the custom validation I built for that string.
example:
Tags is input in the property name
So the value input to the property value should be validated by the tags block I created to make sure the string meets the criteria.

Related

src-resolve: Cannot resolve the name 'PkReportsTo' to a(n) 'identity constraint' component. [XSD]

I was trying to create FK and PK in the XML schema, but it is showing an error in the featured code above. Is shows the message: src-resolve: Cannot resolve the name 'PkReportsTo' to a(n) 'identity constraint' component. [XSD]
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.ricardoterra.com.br"
elementFormDefault="qualified">
<xs:element name="employees">
<xs:complexType>
<xs:sequence>
<xs:element name="employee" maxOccurs="unbounded">
<xs:complexType>
<xs:all>
<xs:element name="firstName" type="xs:string"></xs:element>
<xs:element name="lastName" type="xs:string"></xs:element>
<xs:element name="extension">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="([x][1-9])\w+"></xs:pattern>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="email">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="([a-z])\w+(#)([a-z])\w+(.[a-z]+)*"></xs:pattern>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="officeCode" type="xs:string"></xs:element>
<xs:element name="jobTitle" type="xs:string"></xs:element>
<xs:element name="reportsTo" type="xs:integer" minOccurs="0">
</xs:element>
</xs:all>
<xs:attribute name="number" type="xs:integer"></xs:attribute>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:unique name="number">
<xs:selector xpath="employee"></xs:selector>
<xs:field xpath="#number"></xs:field>
</xs:unique>
<xs:key name="PkEmploye">
<xs:selector xpath="employee"/>
<xs:field xpath="#number"></xs:field>
</xs:key>
<xs:key name="PkReportsTo">
<xs:selector xpath="employee/reportsTo"></xs:selector>
<xs:field xpath="."></xs:field>
</xs:key>
<xs:keyref name="FKtoReportsToEmployee" refer="PkReportsTo">
<xs:selector xpath="employee"></xs:selector>
<xs:field xpath="#number"></xs:field>
</xs:keyref>
</xs:element>
</xs:schema>
I am with a error at the part of my code:
<xs:keyref name="FKtoReportsToEmployee" refer="PkReportsTo">
<xs:selector xpath="employee"></xs:selector>
<xs:field xpath="#number"></xs:field>
</xs:keyref>
I don't know why.
I think you want the target namespace of the schema to be http://www.ricardoterra.com.br so use
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.ricardoterra.com.br"
targetNamespace="http://www.ricardoterra.com.br"
elementFormDefault="qualified">
and that error is likely to go away.

Linq query throws NullReferenceException

Consider the following two pieces from an XSD.
1.
<xs:complexType name="CurrencyAndAmount">
<xs:simpleContent>
<xs:extension base="ActiveOrHistoricCurrencyAndAmount_SimpleType">
<xs:attribute name="Ccy" type="CurrencyCode" use="optional"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
2.
<xs:simpleType name="CurrencyCode">
<xs:restriction base="xs:string">
<xs:maxLength value="3"/>
<xs:pattern value="[A-Z]{3,3}"/>
</xs:restriction>
</xs:simpleType>
I have the value of the "type" attribute in the first complexType element group.
I am trying to get the element group where the "name" attribute has the same value than the the "type" attribute in group 1.
I have this LINQ query...
IEnumerable<XElement> a = xsdDocument.Descendants()
.Where(x => x.Attribute("name").Value == "CurrencyCode");
... but it throws a NullReferenceException

XSD key not in root element and complex keys

I searched for this, but can't find solution to my problem, so please don't trash this question.
We have a service with very complex XML configuration which is described by XSD. There are many rules that must be obeyed for things to run smoothly. XSD describes structure of configuration, but not the rules, which we must do now. After creating some global rules we have to do some more complex now and we encountered a problem. It seams like validator only uses key/keyref/unique from root element. I've created small XSD and XML file to illustrate that:
XSD:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="http://test.org/XMLSchema.xsd"
elementFormDefault="qualified"
xmlns="http://test.org/XMLSchema.xsd"
xmlns:t="http://test.org/XMLSchema.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Orders" type="t:OrdersList">
<xs:key name="OrderNo">
<xs:selector xpath="./t:Order" />
<xs:field xpath="#Number" />
</xs:key>
</xs:element>
<xs:complexType name="OrdersList">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="Order" nillable="false" type="t:Order" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="Order">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="Lines" nillable="false" type="t:OrdersLinesList" />
</xs:sequence>
<xs:attribute name="Number" use="optional" type="xs:string" />
<xs:attribute name="ClientId" use="optional" type="xs:int" />
</xs:complexType>
<xs:complexType name="OrdersLinesList">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="Line" nillable="false" type="t:OrderLine">
<!-- THE PROBLEM -->
<xs:key name="LineNoKey">
<xs:selector xpath="./t:Line" />
<xs:field xpath="#LineNumber" />
</xs:key>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="OrderLine">
<xs:attribute name="LineNumber" use="optional" type="xs:string" />
<xs:attribute name="ProductId" use="optional" type="xs:int" />
<xs:attribute name="Amount" use="optional" type="xs:decimal" />
</xs:complexType>
</xs:schema>
XML:
<?xml version="1.0" encoding="utf-8"?>
<Orders xmlns="http://test.org/XMLSchema.xsd">
<Order Number="0001/5/13" ClientId="123">
<Lines>
<Line LineNumber="1" ProductId="123" Amount="4" />
<Line LineNumber="2" ProductId="124" Amount="4" />
</Lines>
</Order>
<Order Number="0002/5/13" ClientId="123">
<Lines>
<Line LineNumber="1" ProductId="123" Amount="4" />
<!-- Duplicate number - it DOES validate as expected. -->
<Line LineNumber="1" ProductId="124" Amount="4" />
</Lines>
</Order>
<!-- Duplicate number - it doesn't validate as expected. -->
<Order Number="0002/5/13" ClientId="123">
<Lines>
<Line LineNumber="1" ProductId="123" Amount="4" />
<Line LineNumber="2" ProductId="124" Amount="4" />
</Lines>
</Order>
</Orders>
I have a few questions which I can't find any sensible answer:
How to solve the above. LineNumber must be unique, but only within Order/Lines.
Is it possible to allow keyref to contain non existing virtual value (our service uses some predefined... let say things. Custom ones are defined in XML to allow extending functionality, but in most of the cases, users will use predefined ones)
Is there any way to determine if list of elements within a tag (something like Lines above) has at least one tag with attribute set to certain value. Good example would be to describe it like a list of tables with unique names, that has list of columns with unique names and at least one of the columns is a primary key.
This is the corrected constraint for Lines/Line:
<xs:complexType name="Order">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="Lines" nillable="false" type="t:OrdersLinesList">
<!-- THE PROBLEM SOLVED -->
<xs:key name="LineNoKey">
<xs:selector xpath="t:Line"/>
<xs:field xpath="#LineNumber"/>
</xs:key>
</xs:element>
</xs:sequence>
<xs:attribute name="Number" use="optional" type="xs:string"/>
<xs:attribute name="ClientId" use="optional" type="xs:int"/>
</xs:complexType>
If always helps if you can visualize your constraints, to understand the scope they act upon:
The correct diagram:
vs. the original one:
You can see that the selector rooted in the element Line is looking for yet another Line (./t:Line); and even if you fix the selector, it'll always match at most one attribute. The idea of a key is that the selector should match a set of nodes among which the field must be present and unique.
The above should take care of 1.
No.
Not based on an attribute. To stick with your parallel, for XSD 1.0 I would probably enforce an element called PrimaryKey, which should contain one or more references to names of the other columns; as if instead of using the PRIMARY KEY constraint clause in a column definition, I would enforce the use of the same at the table level.

case-insensitive key in xsd

I have an XSD embedded into an XML like this:
<Replacements>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Replacements">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Replace" />
</xs:sequence>
</xs:complexType>
<xs:key name="ReplaceKey">
<xs:selector xpath="./Replace"/>
<xs:field xpath="#old"/>
</xs:key>
</xs:element>
<xs:element name="Replace">
<xs:complexType>
<xs:attribute name="old" type="AnythingButLowerCase" use="required" />
<xs:attribute name="new" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:simpleType name="AnythingButLowerCase">
<xs:restriction base="xs:string">
<xs:pattern value="[^a-z]+"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
<Replace old="A1020____9" new="A1020"/>
<Replace old="a1020____9" new="A1020"/>
</Replacements>
I've used xs:key to define a unique-key on "old" attribute of Replace elements.
my problem is I want this key to be CASE-INSENSITIVE.
I've read so many documents indicating I can use xsd functions like upper-case or translate to solve this, but if I write something like
<xs:field xpath="upper-case(#old)"/>
VS2010 gives me a warning like this:
'upper-case(#old)' is an invalid XPath for selector or field.
What is it I'm doing wrong?
Thanks :)
The correct XPath function is "upper-case", not "upper case". Just add in the hyphen.
OK!
I got that!
Using functions in an xpath for a xs:field is not allowed.
The workaround for what I was seeking is to define a simpleType and put a restriction there not allowing lower-case letters.
Something like this:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<Replacements>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Replacements">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Replace" />
</xs:sequence>
</xs:complexType>
<xs:key name="ReplaceKey">
<xs:selector xpath="./Replace"/>
<xs:field xpath="#old"/>
</xs:key>
</xs:element>
<xs:element name="Replace">
<xs:complexType>
<xs:attribute name="old" type="AnythingButLowerCase" use="required" />
<xs:attribute name="new" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:simpleType name="AnythingButLowerCase">
<xs:restriction base="xs:string">
<xs:pattern value="[^a-z]+"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
<Replace old="A1020____9" new="A1020"/>
<Replace old="a1020____9" new="A1020"/>
</Replacements>
Now this serves as what I want it to.

Why won't this Schema validate this XML file?

The XML file:
<Lista count="3">
<Pelicula nombre="Jurasic Park 3">
<Genero>Drama</Genero>
<Director sexo="M">Esteven Spielberg</Director>
<Temporada>
<Anho>2002</Anho>
<Semestre>Verano</Semestre>
</Temporada>
</Pelicula>
<Pelicula nombre="Maldiciones">
<Genero>Ficcion</Genero>
<Director sexo="M">Pedro Almodovar</Director>
<Temporada>
<Anho>2002</Anho>
<Semestre>Verano</Semestre>
</Temporada>
</Pelicula>
<Pelicula nombre="Amor en New York">
<Genero>Romance</Genero>
<Director sexo="F">Katia Hertz</Director>
<Temporada>
<Anho>2002</Anho>
<Semestre>Verano</Semestre>
</Temporada>
</Pelicula>
</Lista>
And here's the XML Schema file I made, it's not working. :\
<xsd:complexType name="Lista">
<xsd:attribute name="count" type="xsd:integer" />
<xsd:complexContent>
<xsd:element name="Pelicula" type="xsd:string">
<xsd:attribute name="nombre" type="xsd:string" />
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Genero" type="generoType"/>
<xsd:element name="Director" type="directorType">
<xsd:attribute name="sexo" type="sexoType"/>
</xsd:element>
</xsd:element name="Temporada">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Anho" type="anhoType" />
<xsd:element name="Semestre" type="semestreType" />
</xsd:sequence>
</xsd:complexType>
<xsd:element></xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:complexContent>
</xsd:complexType>
<xsd:simpleType name="sexoType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="F"/>
<xsd:enumeration value="M"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="directorType">
<xsd:restriction base="xsd:string" />
</xsd:simpleType>
<xsd:simpleType name="generoType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Drama"/>
<xsd:enumeration value="Accion"/>
<xsd:enumeration value="Romance"/>
<xsd:enumeration value="Ficcion"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="semestreType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Verano"/>
<xsd:enumeration value="Invierno"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="anhoType">
<xsd:restriction base="xsd:integer">
<xsd:minInclusive value="1970"/>
<xsd:maxInclusive value="2020"/>
</xsd:restriction>
</xsd:simpleType>
Try declaring and using your types separately. This makes the XSD a bit longer, but less nested and more readable (and more reusable, too):
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- document element -->
<xs:element name="Lista" type="listaType" />
<!-- type definitions -->
<xs:complexType name="listaType">
<xs:sequence>
<xs:element name="Pelicula" type="peliculaType" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="count" type="countType" />
</xs:complexType>
<xs:complexType name="peliculaType">
<xs:all>
<xs:element name="Genero" type="generoType" />
<xs:element name="Director" type="directorType" />
<xs:element name="Temporada" type="temporadaType" />
</xs:all>
<xs:attribute name="nombre" type="xs:string" />
</xs:complexType>
<xs:complexType name="directorType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="sexo" type="sexoType" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="temporadaType">
<xs:all>
<xs:element name="Anho" type="anhoType" />
<xs:element name="Semestre" type="semestreType" />
</xs:all>
</xs:complexType>
<xs:simpleType name="sexoType">
<xs:restriction base="xs:string">
<xs:enumeration value="F" />
<xs:enumeration value="M" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="generoType">
<xs:restriction base="xs:string">
<xs:enumeration value="Drama" />
<xs:enumeration value="Accion" />
<xs:enumeration value="Romance" />
<xs:enumeration value="Ficcion" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="semestreType">
<xs:restriction base="xs:string">
<xs:enumeration value="Verano" />
<xs:enumeration value="Invierno" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="anhoType">
<xs:restriction base="xs:integer">
<xs:minInclusive value="1970" />
<xs:maxInclusive value="2020" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="countType">
<xs:restriction base="xs:integer">
<xs:minInclusive value="0" />
</xs:restriction>
</xs:simpleType>
</xs:schema>
The above validates your input XML.
Note that:
The convention for the XML Schema namespace prefix is xs (AFAIK - you can still go on and use xsd if you prefer).
The count attribute is really unnecessary, since the count can a) easily be calculated and b) there is a risk of something going wrong when the count attribute value and the actual count differ for some reason. Things that are derivable from the data should never be part of the data.
Oh, and to answer your initial question (why does mine not work):
You never declare an actual document element ("Lista"), you just declare its type. Compare with my solution.
In the complexType name="Lista":
attribute cannot be the first child of a complex type. Attributes must be declared after everything else.
complexContent cannot contain element.
in fact, you don't need a complexContent at all - just use a sequence instead.
In element name="Pelicula":
The type attribute is illegal when you declare a complexType within.
In the complexType for "Pelicula":
Again, attributes last.
Don't use a sequence unless you want to make any other order of children illegal. In this type of document I would guess child order is irrelevant.
In element name="Director":
You can't declare any attributes since when you already declared a type. Include the sexo attribute in the directorType
In simpleType name="directorType":
This should in reality be a complexType containing simpleContent with an extension. This way you can include the sexo attribute
Your XSD isn't even well formed XML.
Your XML iswas not well-formed, either. I have fixed it to be able to test in the first place.
P.S.: There is enough XSD documentation freely available to fix many of the basic problems you've had. There are XSD validators on the Net that help you by telling you what constructs are illegal. Knowing everything is absolutely not necessary, but a little reading + trial and error would have helped. ;-)
For a start:
<Semestre>Verano<Semestre>
...doesn't look well-formed.

Resources