Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make XML elements required based on attribute values in XSD

Tags:

xml

xsd

msxml

My requirement is to have an XSD file which checks the elements based on attribute values. I was able to write XSD up to a point where I can restrict the attribute values of Application/@Type. Can anyone help me to complete the XSD file where I can make some elements required based on the Application/@Type attribute?

I want to make

  • PackageArg required only when Application/@Type is "Batch"
  • Version required only when Application/@Type is "Service"
  • Project required only when Application/@Type is Web" or "Service"

XML File

<Applications>        
    <Application Name="ConfigManagement" Type="Web">            
        <ProjectDirName>ConfigManagement</ProjectDirName>
        <Project>Web.ConfigManagement.csproj</Project>
        <OutputDirName>ConfigManagement</OutputDirName>            
    </Application>
    <Application Name="Util" Type="Web">            
        <ProjectDirName>Web</ProjectDirName>
        <Project>Web.csproj</Project>        
        <OutputDirName>Util</OutputDirName>    
    </Application>
    <Application Name="ConfigService" Type="Service">
        <ProjectDirName>WebServices\ConfigService</ProjectDirName>
        <Project>ConfigService.csproj</Project>    
        <Version>2015\04</Version>
        <OutputDirName>ConfigService</OutputDirName>
    </Application>
    <Application Name="DeliverEmail" Type="Batch">        
        <ProjectDirName>\Batch\DeliverEmail</ProjectDirName>
        <PackageArg>Release</PackageArg>        
        <OutputDirName>Tidal\DeliverEmail</OutputDirName>            
    </Application>
</Applications>

XSD File

<xs:element name="Applications" maxOccurs="1">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="Application" maxOccurs="unbounded" minOccurs="1">
        <xs:complexType>
          <xs:all>
            <xs:element type="xs:string" name="ProjectDirName"/>
            <xs:element type="xs:string" name="Project" minOccurs="0"/>
            <xs:element type="xs:string" name="Version" minOccurs="0"/>
            <xs:element type="xs:string" name="PackageArg" minOccurs="0"/>
            <xs:element type="xs:string" name="OutputDirName"/>
          </xs:all>
          <xs:attribute type="xs:string" name="Name" use="optional"/>
          <xs:attribute name="Type" use="required">
            <xs:simpleType>
              <xs:restriction base="xs:string">
                <xs:enumeration value="Web"/>
                <xs:enumeration value="Batch"/>
                <xs:enumeration value="Service"/>
              </xs:restriction>
            </xs:simpleType>
          </xs:attribute>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
</xs:element>
like image 381
Krishna Avatar asked Mar 19 '26 13:03

Krishna


1 Answers

Update: OP has edited question to remove the use of xs:assert and has stated in comments that validation must take place in C#. The answer to OP's question now becomes:

You cannot enforce a constraint that varies the requiredness of an element based on an attribute value using XSD 1.0, and Microsoft does not support XSD 1.1, therefore you must either relax your constraints or check them outside of your XSD.


Original XSD 1.1 answer

(Retained for benefit of future readers)

You're close, but your assertion,

      <xs:assert test="count(./PackageArg[@type eq 'Batch']) eq 1"/>

tests for @type on PackageArg when it should test for @Type on Application.

The following XSD will validate your XML and enforce your attribute-dependent requirements:

XSD 1.1

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
           vc:minVersion="1.1">
  <xs:element name="Applications">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Application" maxOccurs="unbounded" minOccurs="1">
          <xs:complexType>
            <xs:all>
              <xs:element type="xs:string" name="ProjectDirName"/>
              <xs:element type="xs:string" name="Project" minOccurs="0"/>
              <xs:element type="xs:string" name="Version" minOccurs="0"/>
              <xs:element type="xs:string" name="PackageArg" minOccurs="0"/>
              <xs:element type="xs:string" name="OutputDirName"/>
            </xs:all>
            <xs:attribute type="xs:string" name="Name" use="optional"/>
            <xs:attribute name="Type" use="required">
              <xs:simpleType>
                <xs:restriction base="xs:string">
                  <xs:enumeration value="Web"/>
                  <xs:enumeration value="Batch"/>
                  <xs:enumeration value="Service"/>
                </xs:restriction>
              </xs:simpleType>
            </xs:attribute>
            <xs:assert test="PackageArg or @Type != 'Batch'"/>
            <xs:assert test="Version or @Type != 'Service'"/>
            <xs:assert test="Project or not(@Type = 'Web' or @Type = 'Service')"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Be aware that Microsoft does not support XSD 1.1. (You've tagged your question with 'msxml').

like image 153
kjhughes Avatar answered Mar 22 '26 15:03

kjhughes