XSD Schema - how to ensure that two simple elements either have values or do not have values together - xsd

please assist, this is what i want to achieve in validating my xml file:
<?xml version="1.0" encoding="UTF-8"?>
<worker>
<name>dingo</name>
<ssn>12345</ssn>
</worker>
I want to ensure that the two simple elements 'name' and 'ssn' either have values (as a group) or do not have any value (as a group). They cannot exist individually with a value.
I have to use an XSD schema, so cannot use other options i see suggestions sometimes: Relax NG etc.
I looked into creating a group for elements 'name' and 'ssn' but i am unable to find out how to create a restriction for this group to obtain my condition.
My current XSD file:
<xs:complexType name="worker">
<xs:sequence>
<xs:element name="name" type="xs:string" minOccurs="0" "maxOccurs="1">
<xs:element name="ssn" type="xs:positiveInteger" minOccurs="0" "maxOccurs="1">
</xs:sequence>
</xs:complexType>

<xs:complexType name="worker">
<xs:sequence minOccurs="0">
<xs:element name="name" type="xs:string">
<xs:element name="ssn" type="xs:positiveInteger">
</xs:sequence>
</xs:complexType>

You have to do
<xs:complexType name="worker">
<xs:group ref="workerGrp" minOccurs="0"/>
</xs:complexType>
<xs:group name="workerGrp">
<xs:sequence>
<xs:element name="name" type="xs:string">
<xs:element name="ssn" type="xs:positiveInteger">
</xs:sequence>
</xs:group>

Related

Create an XSD that supports something like web.config transforms or multi-tenant/configuration xml files

I have an Xml file like this:
<Configuration xmlns="http://schemas.benefittech.com/evolution/site">
<SiteSettings>
<ProfileGroup>TBOLoanPmtElection</ProfileGroup>
<AWSGroup>TBOLoanPmtElection_Admin</AWSGroup>
</SiteSettings>
</Configuration>
And I have an XSD file like this:
<?xml version="1.0" encoding="us-ascii" ?>
<xs:schema elementFormDefault="qualified" attributeFormDefault="unqualified" version="5.0"
xmlns="http://schemas.benefittech.com/evolution/site" targetNamespace="http://schemas.benefittech.com/evolution/site"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:vs="http://schemas.benefittech.com/Visual-Studio-Intellisense" vs:friendlyname="Evolution Site Configuration">
<xs:element name="Configuration">
<xs:complexType>
<xs:all>
<xs:element name="SiteSettings" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:all>
<xs:element name="AWSGroup" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="ProfileGroup" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="LogActivityGroup" type="xs:string" minOccurs="0" maxOccurs="1"/>
</xs:all>
</xs:complexType>
</xs:element>
</xs:all>
</xs:complexType>
</xs:element>
</xs:schema>
I want to enable my Xml to look something like the following to allow for 'environment overrides' (the E attribute on the env:* element):
<Configuration xmlns="http://schemas.benefittech.com/evolution/site" xmlns:env="http://schemas.benefittech.com/evolution/siteenvironment">
<SiteSettings>
<ProfileGroup>TBOLoanPmtElection</ProfileGroup>
<AWSGroup>TBOLoanPmtElection_Admin</AWSGroup>
<env:AWSGroup E="SecondEnvironment">TBOLoanPmtElection2_Admin</env:AWSGroup>
</SiteSettings>
</Configuration>
Is there a way to modify the XSD so that given EVERY element in my configuration, I want to optionally have a env:* version of it. Do I have to make a new schema file like below that is basically a duplicate of original except for the target namespace?
<?xml version="1.0" encoding="us-ascii" ?>
<xs:schema elementFormDefault="qualified" attributeFormDefault="unqualified" version="5.0"
xmlns="http://schemas.benefittech.com/evolution/siteenvironment" targetNamespace="http://schemas.benefittech.com/evolution/siteenvironment"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:vs="http://schemas.benefittech.com/Visual-Studio-Intellisense" vs:friendlyname="Evolution Site Configuration">
<xs:element name="Configuration">
<xs:complexType>
<xs:all>
<xs:element name="SiteSettings" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:all>
<xs:element name="AWSGroup" type="xs:string" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="E" use="required" type="xs:string"/>
</xs:complexType>
</xs:element>
<xs:element name="ProfileGroup" type="xs:string" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="E" use="required" type="xs:string"/>
</xs:complexType>
</xs:element>
<xs:element name="LogActivityGroup" type="xs:string" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="E" use="required" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:all>
</xs:complexType>
</xs:element>
</xs:all>
</xs:complexType>
</xs:element>
</xs:schema>
Is there a way to modify the XSD so that given EVERY element in my configuration, I want to optionally have a env:* version of it.
No, there is no elegant way to add, after each element, an optional element having the same name but a different namespace. You would need to add each such env:* element individually.
Do I have to make a new schema file like below that is basically a duplicate of original except for the target namespace?
Yes, but only if you stick to this plan of using a different namespace for the 'extension' elements. In your reply to my comment you said
I thought I'd need separate namespaces. I didn't want to change code everywhere that selects elements. For example, SiteConfig.Elements( "SiteSettings" ).Elements( "AWSGroup" ).FirstOrDefault(); Would continue to work without picking up the env:* elements. Then during the publish process, I would swap out the env:* properties everywhere and code would just work. What's your suggestion?
I've hardly written a line of C#, but according to https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.firstordefault?view=netcore-3.1 the FirstOrDefault() method returns the first element in the sequence or a default. So I think there is a much simpler approach that will not break the existing code...
On each element that needs to have option extension element(s):
Set maxOccurs="unbounded" (or "2", if you only want to allow one extension element)
Add the attribute "E" and make it optional (so that existing documents which omit it on the first occurrence are still valid).
The name "E" for the attribute is not ideal - I would pick something more descriptive if possible.

XSD field dependent on another field [duplicate]

I have an XSD to validate an XML file. The structure is as follows:
<root>
<child>
<size>2</size>
<childElement>Element 1</childElement>
<childElement>Element 2</childElement>
</child>
</root>
The number of childElements is dependent on the size provided i.e. if size was set as 3, not more than 3 childElements can be added.
I have tried using xs:alternative but it does not seem to work:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="child" minOccurs="1" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="size" type="xs:integer" maxOccurs="1"/>
<xs:element name="childElement" type="xs:string" maxOccurs="1">
<xs:alternative test="#size>1" maxOccurs="#size"/>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Is there a way of using xs:alternative or another tag to achieve this, or is this outside the realm of possibility with XSD?
Design recommendation: If your XML design can still be changed, eliminate the size element and convey that information implicitly rather than explicitly. By eliminating the duplication of information, you'll not need to check that the duplication is consistent.
If your XML design cannot still be changed, or if you choose not to change it...
XSD 1.0
Not possible. Would have to be checked out-of-band wrt XSD.
XSD 1.1
Possible using xs:assert:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified"
elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
vc:minVersion="1.1">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="child">
<xs:complexType>
<xs:sequence>
<xs:element name="size" type="xs:integer"/>
<xs:element name="childElement" maxOccurs="unbounded"/>
</xs:sequence>
<xs:assert test="count(childElement) = size"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

After importing a complextype in one xsd from another xsd, is it possible to have selected subelements of the complex type in the second xsd

After importing a complextype in one xsd from another xsd, is it possible to have selected subelements of the complex type in the second xsd
What I mean is :
I have first xsd. -> AddressFile.xsd .In AddressFile.xsd, there is a complex type IndividualAddress, with 5 subelemnts. In the next xsd, Individual.xsd , I am referring to the complext type IndividualAddress in AddressFile.xsd. But in Individual.xsd, I just want DoorNO , StreetNum and State from the complex Type IndividualAddress directly . Is that possible. Is there any restrictions I can use.
AddressFile.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://abc/DP/Address">
<xs:complexType name="IndividualAddress">
<xs:annotation>
<xs:documentation>Address of an individual
</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="DoorNO" type="xs:String"
nillable="true" minOccurs="0"/>
<xs:element name="StreetNum" type="xs:String"
nillable="true" minOccurs="0"/>
<xs:element name="State" type="xs:String"
nillable="true" minOccurs="0"/>
<xs:element name="Country" type="xs:String"
nillable="true" minOccurs="0"/>
<xs:element name="Pin" type="xs:String"
nillable="true" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
Individual.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:address="http://abc/DP/Address"
targetNamespace="http://abc/DP/Individual">
<xs:import namespace="http://abc/DP/Address"
schemaLocation="AddressFile.xsd"/>
<xs:complexType name="Individual">
<xs:annotation>
<xs:documentation>Address of an individual
</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="Name" type="xs:String"
nillable="true" minOccurs="0"/>
<xs:element name="Age" type="xs:int"
nillable="true" minOccurs="0"/>
<xs:element name="DoorNO" type=" address:IndividualAddress "
nillable="true" minOccurs="0"/>
<xs:element name="StreetNum" type=" address:IndividualAddress "
nillable="true" minOccurs="0"/>
<xs:element name="State" type=" address:IndividualAddress "
nillable="true" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
Your best approach depends on what you are trying to accomplish.
If you want XSD-aware tools to know that complex type Individual is derived from complex type IndividualAddress, you can derive the one from the other. You will need to do so in two steps: a restriction step to get rid of the unwanted child elements of the base type and then an extension step to add more elements, at the end. (Not at the beginning.)
If you just want to reuse the elements, the creator of the base schema has done the best job they can to hinder you, by making all the children of IndividualAddress local types, which cannot be referred to from elsewhere. If they wanted to make reuse easy, or even possible, they would (a) make the elements in question top-level elements, and/or possibly (b) use named model groups to package up groups of useful elements to be used together.

Schema with elements, attributes, and text

I am having trouble getting this XML file to validate against my schema, which doesn't have syntax errors according to my XML editor. I am trying to make course an complexType element, but it keeps telling me I can't. The XML is correct, it is definitely something with my schema, I just can't figure it out.
Here is the XML:
<?xml version="1.0" encoding="utf-8"?>
<courses xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="course_offerings.xsd">
<course id="WEB225">
<name>Web Development II</name>
<offered>Spring</offered>
<pre_reqs>WEB125</pre_reqs>
</course>
<course id="WEB125">
<name>Web Development I</name>
<offered>Fall</offered>
</course>
<course id="WEB325">
<name>Client-Side Scripting</name>
<offered>Spring</offered>
<offered>Fall</offered>
<pre_reqs>WEB225</pre_reqs>
</course>
</courses>
And here is my schema:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="courses">
<xs:complexType>
<xs:sequence>
<xs:element name="course" type="xs:string"/>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="offered" type="xs:string"/>
<xs:element name="pre_reqs" type="xs:string"/>
</xs:sequence>
</xs:sequence>
<xs:attribute name="id" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:schema>
Since you haven't mentioned what error you are getting, I'm providing what I can observe..
In your XML you have included this statement: xsi:noNamespaceSchemaLocation="course_offerings.xsd" This means it is your default XML schema. You need to verify the name of schema and make sure it is present in default path.. (same as that of XML file). Otherwise you may end up seeing an error unable to locate schema
course_offerings.xsd
You have declared <xs:element name="course" type="xs:string"/> as string .. that should not be the case.. In your XML it's a complexType, ie, an element having child elements inturn..
All these elements name, offered, pre_reqs should come under this complexType
Attribute should be within the scope of this complexType..
Otherwise you would face not just one but multiple errors since the definition of element course is invalid
Refer the sample XSD below:
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="courses">
<xs:complexType>
<xs:sequence>
<xs:element name="course">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="offered" type="xs:string"/>
<xs:element name="pre_reqs" type="xs:string"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
The above mentioned style is hierarchical, there is an alternative method to write a schema file.. If you understand the current consequences and if you wish to know further then I will let you know..
For now this much explanation should be good..

Question about xsd. Elements inside complex type must be allowed in any order

If you look at the following xsd fragment you can conclude that the corresponding xml will first contain cars followed by busses eg:
car,car,bus,bus
HOWEVER I want the xml to be able to contain
car,bus,car,bus
What change do I need to make in the xsd below in order to achieve this?
<xs:element name="body">
<xs:complexType>
<xs:sequence>
<xs:element name="session" type="tns:session" />
<xs:element minOccurs="0" maxOccurs="unbounded" name="car" type="tns:car" />
<xs:element minOccurs="0" maxOccurs="unbounded" name="bus" type="tns:bus" />
</xs:sequence>
</xs:complexType>
</xs:element>
It's a bit cumbersome, but you might achieve what you're looking for like this:
create a <xs:choice> element with your car and bus elements inside; this defines that one of the contained elements can be used
make sure to have the attribtues minOccurs=1 and maxOccurs=unbounded on that <xs:choice> - this gives you any number of either car or bus elements - any number, any combination
So your XML schema would look something like this (I added some stuff just to be able to generate a sample XML and verify it works - tweak as needed):
<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="body">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element minOccurs="0" maxOccurs="unbounded" name="car" type="CarType" />
<xs:element minOccurs="0" maxOccurs="unbounded" name="bus" type="BusType" />
</xs:choice>
</xs:complexType>
</xs:element>
<xs:complexType name="CarType">
<xs:sequence>
<xs:element name="Maker" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="BusType">
<xs:sequence>
<xs:element name="Maker" type="xs:string" />
<xs:element name="Capacity" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:schema>
use <xs:any> insted of <xs:sequence>

Resources