IXmlSerializable验证模式

时间:2014-01-22 13:43:38

标签: c# xml serialization xsd schema

我正在为我的类的自定义(反)序列化逻辑实现IXmlSerializable,但希望根据XSD架构检查XML的读写。我添加了XmlSchemaProviderAttribute

[XmlSchemaProvider("ConfigSchema")]
[XmlRoot("Config")]
public class Config
{
    // properties, fields and methods incl. interface methods

    public static XmlQualifiedName ConfigSchema(XmlSchemaSet xs)
    {
        const string xsdPath = "./Config.xsd";

        var serializer = new XmlSerializer(typeof(XmlSchema));
        var schema = (XmlSchema)serializer.Deserialize(new XmlTextReader(xsdPath), null);

        xs.XmlResolver = new XmlUrlResolver();
        xs.Add(schema);

        return new XmlQualifiedName("Config", "namespace");
    }
}

架构加载正常,(de)序列化按预期工作,但没有验证。

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="ConfigSchema" 
    targetNamespace="namespace"
    elementFormDefault="qualified"
    xmlns="namespace"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:complexType name="Config">
        <xs:all>
            <xs:element name="Config">
                <xs:complexType>
                    <xs:all>
                        <xs:element name="name" type="xs:string" />
                        <xs:element name="timeout" type="xs:time" />
                        <xs:element name="points" minOccurs="1">
                            <xs:complexType>
                                <xs:sequence>
                                    <xs:element name="point" minOccurs="1" maxOccurs="unbounded">
                                        <xs:complexType>
                                            <xs:all>
                                                <xs:element name="a" type="xs:int" />
                                                <xs:element name="b" type="xs:int" />
                                                <xs:element name="c" type="xs:int" />
                                            </xs:all>
                                        </xs:complexType>
                                    </xs:element>
                                </xs:sequence>
                            </xs:complexType>
                        </xs:element>
                    </xs:all>
                </xs:complexType>
            </xs:element>
        </xs:all>
    </xs:complexType>
</xs:schema>

如果我针对上述模式反序列化以下内容,则不会抛出异常(<timeout>缺失,但在模式中的<xs:all>下指定):

<?xml version="1.0" encoding="utf-8" ?>
    <Config
        xmlns:xsi="http://w3.org/2001/XMLScehma-instance"
        xsi:schemaLocation="namespace Config.xsd">
        <name>some name</name>
        ...

编辑:以下是我如何运行

string path = "./serviceconfig.xml";
var serializer = new XmlSerializer(typeof(Config));
var cfg = (Config)serializer.Deserialize(new XmlTextReader(path), null);

根据架构验证incomming / outgoing XML的“正确”/“最佳”方法是什么?

编辑#2:更多信息

这是完整的XML

<?xml version="1.0" encoding="utf-8" ?>
    <Config
        xmlns:xsi="http://w3.org/2001/XMLScehma-instance"
        xsi:schemaLocation="namespace Config.xsd">
        <name>some name</name>
        <timeout>10</timeout>
        <points>
            <point>
                <a>5</a>
                <b>7</b>
                <c>11</c>
            </point>
            <point>
                <a>8</a>
                <b>7</b>
                <c>3</c>
            </point>
        </points>
    </Config>

我已尝试查看ReadXml(XmlReader reader)中的XmlReaderSettings,但以下内容没有帮助

var settings = new XmlReaderSettings
{
    ValidationType = ValidationType.Schema,
    Schemas = _schemaSet
};

settings.ValidationEventHandler += (sender, args) => Console.WriteLine(args.Message);
reader = XmlReader.Create(reader, settings);

_schema是根据上面的静态ConfigSchema()设置的。

2 个答案:

答案 0 :(得分:4)

XML未验证,因为您的架构指定了targetNamespace = "namespace",但您的XML实例未使用xmlns引用此命名空间。

因此验证器不知道如何验证XML,因为它没有意识到您的XML使用模式中定义的类型。

如果您发布完整的架构和xml实例,我将能够制作一个工作样本。

<强>更新

感谢发布您的架构。你在哪里得到它?我问的原因是它没有定义任何根节点,只是一个名为Config的类型。

如果删除外部复杂类型元素:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="ConfigSchema"
    targetNamespace="namespace"
    elementFormDefault="qualified"
    xmlns="namespace"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Config">
    <xs:complexType>
      <xs:all>
        <xs:element name="name" type="xs:string" />
        <xs:element name="timeout" type="xs:time" />
        <xs:element name="points" minOccurs="1">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="point" minOccurs="1" maxOccurs="unbounded">
                <xs:complexType>
                  <xs:all>
                    <xs:element name="a" type="xs:int" />
                    <xs:element name="b" type="xs:int" />
                    <xs:element name="c" type="xs:int" />
                  </xs:all>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:all>
    </xs:complexType>
  </xs:element>
</xs:schema>

我能够验证以下实例:

<?xml version="1.0" encoding="utf-8" ?>
<Config xmlns="namespace">
  <name>some name</name>
  <timeout>13:20:00.000-05:00</timeout>
  <points>
    <point>
      <a>5</a>
      <b>7</b>
      <c>11</c>
    </point>
    <point>
      <a>8</a>
      <b>7</b>
      <c>3</c>
    </point>
  </points>
</Config>

请注意,根据架构,“10”不是超时列的有效值。当我尝试时,我收到了这个错误:

  

'namespace:timeout'元素无效 - 值'10'无效   根据其数据类型“http://www.w3.org/2001/XMLSchema:time” -   字符串'10'不是有效的XsdDateTime值。

答案 1 :(得分:1)

经过多次挖掘并在Hugh的答案帮助下,我能够破解这个坚果。

  1. 我将命名空间添加到我的XML和C#类:

    <?xml version="1.0" encoding="utf-8" ?>
    <Config xlmns="namespace" ...
    

    [XmlSchemaProvider("ConfigSchema")]
    [XmlRoot(Namespace="namespace", ElementName="Config")]
    public class Config : IXmlSerializable
    {
       private static XmlSchemaSet _schema;
    
       public static XmlQualifiedName ConfigSchema(XmlSchemaSet xs)
       {
          _schema = xs;
          // rest of method as OP
       }
    
       public void ReadXml(XmlReader reader)
       {
          var settings = new XmlReaderSettings
          {
             ValidationType = ValidationType.Schema,
             Schemas = _schemas;
          }
    
          settings.ValidationEventHandler += ValidationCallBack;
          reader = XmlReader.Create(reader, settings);
          reader.Read(); // your own read logic
       }
    
       // rest of class
    }
    
  2. 我的XSD错了。我把类型定义与元素结构定义混淆了。

    <?xml version="1.0" encoding="utf-8"?>
    <xs:schema id="ServiceConfigSchema" 
      targetNamespace="namespace"
      elementFormDefault="qualified"
      xmlns="namespace"
      xmlns:xs="http://www.w3.org/2001/XMLSchema">  
    
      <xs:complexType name="point">
        <xs:all>
          <xs:element name="a" type="xs:int" />
          <xs:element name="b" type="xs:int" />
          <xs:element name="c" type="xs:int" />
        </xs:all>
      </xs:complexType>
    
      <xs:complexType name="points">
        <xs:sequence>
          <xs:element name="point" type="point" minOccurs="1" maxOccurs="unbounded" />
        </xs:sequence>
      </xs:complexType>
    
      <xs:complexType name="Config">
        <xs:all>
          <xs:element name="name" type="xs:string" />
          <xs:element name="timeout" type="xs:time" />
          <xs:element name="points" type="points" />
        </xs:all>
      </xs:complexType>
    
      <xs:element name="Config" type="Config" />
    </xs:schema>