HeyCHのブログ

慢性疲労のへいちゃんです

【C#】XmlDocument.Validateが動かないんやけど

皆さんXMLをXSDでValidateすることがたまにあると思うんですが、
すでにXSD的なエラーがXMLに存在する場合、XmlDocument.Validateがちゃんと動いてくれません。
例えば以下のような場合、「ISBN属性が無い」「不明なauthorr要素がある」「不明なbooks 要素がある」という3つのエラーが表示されてほしいわけです。

using System;
using System.Xml;
using System.Xml.Schema;
using System.Xml.XPath;

class XPathValidation
{
    static void Main()
    {
        try
        {
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.Schemas.Add("http://www.contoso.com/books", "contosoBooks.xsd");
            settings.ValidationType = ValidationType.Schema;

            XmlReader reader = XmlReader.Create("contosoBooks.xml", settings);
            XmlDocument document = new XmlDocument();
            document.Load(reader);

            ValidationEventHandler eventHandler = new ValidationEventHandler(ValidationEventHandler);

            // the following call to Validate succeeds.
            document.Validate(eventHandler);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    static void ValidationEventHandler(object sender, ValidationEventArgs e)
    {
        switch (e.Severity)
        {
            case XmlSeverityType.Error:
                Console.WriteLine("Error: {0}", e.Message);
                break;
            case XmlSeverityType.Warning:
                Console.WriteLine("Warning {0}", e.Message);
                break;
        }
    }
}
<bookstore xmlns="http://www.contoso.com/books" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="test.xsd">
    <book genre="autobiography" publicationdate="1981-03-22">
        <title>The Autobiography of Benjamin Franklin</title>
        <author>
            <first-name>Benjamin</first-name>
            <last-name>Franklin</last-name>
        </author>
        <price>8.99</price>
    </book>
    <book genre="novel" publicationdate="1967-11-17" ISBN="0-201-63361-2">
        <title>The Confidence Man</title>
        <authorr>
            <first-name>Herman</first-name>
            <last-name>Melville</last-name>
        </authorr>
        <price>11.99</price>
    </book>
    <books genre="philosophy" publicationdate="1991-02-15" ISBN="1-861001-57-6">
        <title>The Gorgias</title>
        <author>
            <name>Plato</name>
        </author>
        <price>9.99</price>
    </books>
</bookstore>
<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.contoso.com/books" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="bookstore">
        <xs:complexType>
            <xs:sequence>
                <xs:element maxOccurs="unbounded" name="book">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="title" type="xs:string" />
                            <xs:element name="author">
                                <xs:complexType>
                                    <xs:sequence>
                                        <xs:element minOccurs="0" name="name" type="xs:string" />
                                        <xs:element minOccurs="0" name="first-name" type="xs:string" />
                                        <xs:element minOccurs="0" name="last-name" type="xs:string" />
                                    </xs:sequence>
                                </xs:complexType>
                            </xs:element>
                            <xs:element name="price" type="xs:decimal" />
                        </xs:sequence>
                        <xs:attribute name="genre" type="xs:string" use="required" />
                        <xs:attribute name="publicationdate" type="xs:date" use="required" />
                        <xs:attribute name="ISBN" type="xs:string" use="required" />
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>


こういう場合はコードをちょちょっと書き換えて、XmlDocument.Load後にValidateしてやるとうまくいきます。
(Validateが動いていない原因はXmlDocument.Loadでエラーが発生していたから)

using System;
using System.Xml;
using System.Xml.Schema;

namespace XmlSchemaValidate {
    class Program {
        static void Main(string[] args) {
            XmlDocument document = new XmlDocument();
            document.Load("test.xml");

            XmlTextReader reader = new XmlTextReader("test.xsd");
            XmlSchema xmlschema = XmlSchema.Read(reader, null);
            XmlSchemaSet schemaSet = new XmlSchemaSet();
            schemaSet.Add(xmlschema);
            document.Schemas.Add(schemaSet);

            try {
                ValidationEventHandler eventHandler = new ValidationEventHandler(ValidationEventHandler);
                document.Validate(eventHandler);
            } catch (Exception ex) {
                Console.WriteLine(ex.Message);
            }
            Console.Read();
        }
        static void ValidationEventHandler(object sender, ValidationEventArgs e) {
            switch (e.Severity) {
                case XmlSeverityType.Error:
                    Console.WriteLine("Error: {0}", e.Message);
                    break;
                case XmlSeverityType.Warning:
                    Console.WriteLine("Warning {0}", e.Message);
                    break;
            }
        }
    }
}
Error: 必要な属性 'ISBN' が見つかりません。
Error: 要素 名前空間 'http://www.contoso.com/books' の 'book' には無効な子要素 名前空間 'http://www.contoso.com/books'  の 'authorr' が含まれています。必要とされる要素は 名前空間 'http://www.contoso.com/books' の 'author' です。
Error: 要素 名前空間 'http://www.contoso.com/books' の 'bookstore' には無効な子要素 名前空間 'http://www.contoso.com/books' の 'books' が含まれています。必要とされる要素は 名前空間 'http://www.contoso.com/books' の 'book' です。