简体   繁体   中英

Partial XML file validation using XSD

I am trying to use the XDocument class and XmlSchemaSet class to validate an XMl file.

The XML file already exists but I want to add in just a single element consisting of a couple other elements and I only want to validate this node.

Here is an example of the XML file. The piece I would like to validate is the TestConfiguration node:

<?xml version="1.0" encoding="ISO-8859-1"?>
<Root>
  <AppType>Test App</AppType>
  <LabelMap>
    <Label0>
        <Title>Tests</Title>
        <Indexes>1,2,3</Indexes>
    </Label0>
  </LabelMap>

<TestConfiguration>
    <CalculateNumbers>true</CalculateNumbers>
    <RoundToDecimalPoint>3</RoundToDecimalPoint>
</TestConfiguration>
</Root>

Here is my xsd so far:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="TestConfiguration"
           targetNamespace="MyApp_ConfigurationFiles" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="TestConfiguration">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="CalculateNumbers" type="xs:boolean" minOccurs="1" maxOccurs="1"/>
        <xs:element name="RoundToDecimalPoint" type="xs:int" minOccurs="1" maxOccurs="1"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Here is the code I use to validate it:

private bool ValidateXML(string xmlFile, string xsdFile)
{
    string xsdFilePath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) ?? string.Empty, xsdFile);

    Logger.Info("Validating XML file against XSD schema file.");
    Logger.Info("XML: " + xmlFile);
    Logger.Info("XSD: " + xsdFilePath);

    try
    {
        XDocument xsdDocument = XDocument.Load(xsdFilePath);
        XmlSchemaSet schemaSet = new XmlSchemaSet();
        schemaSet.Add(XmlSchema.Read(new StringReader(xsdDocument.ToString()), this.XmlValidationEventHandler));
        XDocument xmlDocument = XDocument.Load(xmlFile);
        xmlDocument.Validate(schemaSet, this.XmlValidationEventHandler);
    }
    catch (Exception e)
    {
        Logger.Info("Error parsing XML file: " + xmlFile);
        throw new Exception(e.Message);
    }

    Logger.Info("XML validated against XSD.");
    return true;
}

Even validating the full XML file, the validation will pass successfully causing me to run into problems when I try to load the XML file into the generated class file created by xsd2code, the error: <Root xmlns=''> was not expected. .

How can I validate just the TestConfiguration piece?

Thanks

You have a few issues here:

  1. Validating the entire document succeeds when it should fail.

    This happens because the root node is unknown to the schema, and encountering an unknown node is considered a validation warning not a validation error - even if that unknown node is the root element. To enable warnings while validating, you need to set XmlSchemaValidationFlags.ReportValidationWarnings . However, there's no way to pass this flag to XDocument.Validate() . The question XDocument.Validate is always successful shows one way to work around this.

    Having done this, you must also throw an exception in your validation handler when ValidationEventArgs.Severity == XmlSeverityType.Warning .

    (As for requiring a certain root element in your XSD, this is apparently not possible .)

  2. You need a convenient way to validate elements as well as documents, so you can validate your <TestConfiguration> piece.

  3. Your XSD and XML are inconsistent.

    You XSD specifies that your elements are in the XML namespace MyApp_ConfigurationFiles in the line targetNamespace="MyApp_ConfigurationFiles" elementFormDefault="qualified" . In fact the XML elements shown in your question are not in any namespace.

    If the XSD is correct, your XML root node needs to look like:

     <Root xmlns="MyApp_ConfigurationFiles"> 

    If the XML is correct, your XSD needs to look like:

     <xs:schema id="TestConfiguration" elementFormDefault="unqualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> 

After you have resolved the XSD and XML inconsistency from #3, you can solve issues #1 and #2 by introducing the following extension methods that validate both documents and elements:

public static class XNodeExtensions
{
    public static void Validate(this XContainer node, XmlReaderSettings settings)
    {
        if (node == null)
            throw new ArgumentNullException();
        using (var innerReader = node.CreateReader())
        using (var reader = XmlReader.Create(innerReader, settings))
        {
            while (reader.Read())
                ;
        }
    }

    public static void Validate(this XContainer node, XmlSchemaSet schemaSet, XmlSchemaValidationFlags validationFlags, ValidationEventHandler validationEventHandler)
    {
        var settings = new XmlReaderSettings();
        settings.ValidationType = ValidationType.Schema;
        settings.ValidationFlags |= validationFlags;
        if (validationEventHandler != null)
            settings.ValidationEventHandler += validationEventHandler;
        settings.Schemas = schemaSet;
        node.Validate(settings);
    }
}

Then, to validate the entire document, do:

try
{
    var xsdDocument = XDocument.Load(xsdFilePath);
    var schemaSet = new XmlSchemaSet();
    using (var xsdReader = xsdDocument.CreateReader())
        schemaSet.Add(XmlSchema.Read(xsdReader, this.XmlSchemaEventHandler));
    var xmlDocument = XDocument.Load(xmlFile);

    xmlDocument.Validate(schemaSet, XmlSchemaValidationFlags.ReportValidationWarnings, XmlValidationEventHandler);
}
catch (Exception e)
{
    Logger.Info("Error parsing XML file: " + xmlFile);
    throw new Exception(e.Message);
}

And to validate a specific node, you can use the same extension methods:

XNamespace elementNamespace = "MyApp_ConfigurationFiles";
var elementName = elementNamespace + "TestConfiguration";

try
{
    var xsdDocument = XDocument.Load(xsdFilePath);
    var schemaSet = new XmlSchemaSet();
    using (var xsdReader = xsdDocument.CreateReader())
        schemaSet.Add(XmlSchema.Read(xsdReader, this.XmlSchemaEventHandler));
    var xmlDocument = XDocument.Load(xmlFile);

    var element = xmlDocument.Root.Element(elementName);
    element.Validate(schemaSet, XmlSchemaValidationFlags.ReportValidationWarnings, this.XmlValidationEventHandler);
}
catch (Exception e)
{
    Logger.Info(string.Format("Error validating element {0} of XML file: {1}", elementName, xmlFile));
    throw new Exception(e.Message);
}

Now validating the entire document fails while validating the {MyApp_ConfigurationFiles}TestConfiguration node succeeds, using the following validation event handlers:

void XmlSchemaEventHandler(object sender, ValidationEventArgs e)
{
    if (e.Severity == XmlSeverityType.Error)
        throw new XmlException(e.Message);
    else if (e.Severity == XmlSeverityType.Warning)
        Logger.Info(e.Message);
}

void XmlValidationEventHandler(object sender, ValidationEventArgs e)
{
    if (e.Severity == XmlSeverityType.Error)
        throw new XmlException(e.Message);
    else if (e.Severity == XmlSeverityType.Warning)
        throw new XmlException(e.Message);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM