Search code examples
c#xmlxsd

Handling XSD validation in .NET Core


I have this sample XSD. It works, but when a validation error occurs, it shows error as shown below. I want the field name as well:

Validation Error: The 'FIELD' element is invalid - The value '12345' is invalid according to its datatype 'Union' - The value '12345' is not valid according to any of the memberTypes of the union.

XSD

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
    <xs:element name="DOCUMENTS">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="DOCUMENT">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="FORM" type="xs:string"/>
                            <xs:element name="HEADER">
                                <xs:complexType>
                                    <xs:sequence>
                                       <xs:element name="FIELD" maxOccurs="unbounded">
                                          <xs:complexType>
                                             <xs:simpleContent>
                                                <xs:extension base="FieldValueType">
                                                    <xs:attribute name="name" type="FieldNameType" use="required"/>
                                                </xs:extension>
                                             </xs:simpleContent>
                                          </xs:complexType>
                                       </xs:element>
                                    </xs:sequence>
                                </xs:complexType>
                            </xs:element>
                            <xs:element name="TABLES" minOccurs="0"/>
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:simpleType name="FieldNameType">
        <xs:restriction base="xs:string">
            <xs:enumeration value="CALC_FILE"/>
            <xs:enumeration value="SQL_ORGNO"/>
            <xs:enumeration value="SQL_RECEIVER"/>
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="FieldValueType">
        <xs:union memberTypes="CALC_FILEType SQL_ORGNOType SQL_RECEIVERType"/>
    </xs:simpleType>

    <xs:simpleType name="CALC_FILEType">
        <xs:restriction base="xs:string">
            <xs:pattern value="[A-Za-z ]+"/> 
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="SQL_RECEIVERType">
        <xs:restriction base="xs:string">
            <xs:pattern value="[A-Za-z ]+"/> 
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="SQL_ORGNOType">
        <xs:restriction base="xs:string">
            <xs:minLength value="5"/>
            <xs:maxLength value="8"/>
        </xs:restriction>
    </xs:simpleType>

</xs:schema>

XML

<?xml version="1.0" encoding="utf-8"?>
<DOCUMENTS>
  <DOCUMENT>
    <FORM>Development_template_DfD_1.8</FORM>
    <HEADER>
      <FIELD name="CALC_FILE">test</FIELD>
      <FIELD name="SQL_ORGNO">12345</FIELD>
      <FIELD name="SQL_RECEIVER">Test AS</FIELD>
    </HEADER>
    <TABLES />
  </DOCUMENT>
</DOCUMENTS>

Solution

  • If you validate a tree like an XmlDocument and dive down deep to get an XmlSchemaValidationException from the validation exception you can cast its SourceObject to an XmlNode and read out its OuterXml (or of course certain attributes if needed e.g. {((e.Exception as XmlSchemaValidationException).SourceObject as XmlNode).Attributes["name"].Value})):

    using System.Xml;
    using System.Xml.Schema;
    
    var xmlSchemaSet = new XmlSchemaSet();
    xmlSchemaSet.Add(null, "schema1.xsd");
    
    
    foreach (var file in new string[] { "valid-sample1.xml", "invalid-sample1.xml" })
    {
        XmlDocument doc = new XmlDocument();
        doc.Load(file);
    
        doc.Schemas.Add(xmlSchemaSet);
    
        doc.Validate((o, e) =>
        {
            Console.WriteLine($"{file}: {e.Message} {((e.Exception as XmlSchemaValidationException).SourceObject as XmlNode).OuterXml}");
        });
    }
    

    For an invalid sample with e.g.

    <DOCUMENTS>
      <DOCUMENT>
        <FORM>Development_template_DfD_1.8</FORM>
        <HEADER>
          <FIELD name="CALC_FILE">test</FIELD>
          <FIELD name="SQL_ORGNO">1234</FIELD>
          <FIELD name="SQL_RECEIVER">Test AS</FIELD>
        </HEADER>
        <TABLES />
      </DOCUMENT>
    </DOCUMENTS>
    

    this outputs e.g.

    invalid-sample1.xml: The 'FIELD' element is invalid - The value '1234' is invalid according to its datatype 'Union' - The value '1234' is not valid according to any of the memberTypes of the union. <FIELD name="SQL_ORGNO">1234</FIELD>