【问题标题】:Why is my XML not validating in a typed XML column in SQL Server 2008?为什么我的 XML 没有在 SQL Server 2008 的类型化 XML 列中验证?
【发布时间】:2011-04-13 22:21:38
【问题描述】:

超短版:

当我快写完问题时,我解决了这个问题。很快就会有答案。

短版:

为什么 SQL Server 拒绝将我的 XML 插入到类型化的 XML 列中? XML 是在 .NET 4 中使用 System.Xml.Serialization.XmlSerializer 生成的。

更长的版本:

我有一个可以使用XmlSerializer 成功序列化的类。更具体地说,我可以使用XmlSerializer 成功序列化该类的数组。

我在 SQL Server 2008 表中有一个 XML 列。如果我只使用无类型的 XML 列,则插入 XmlSerializer.Serialize 的输出可以正常工作(我使用的是实体框架,它将 XML 列转换为类型 string)。但是,我想将该列改为类型化 XML 列。当我这样做时,无论我尝试什么,我似乎都无法从序列化程序中获取 XML 以成功验证。

这是我用来在 SQL Server 中创建类型化 XML 列的(略微混淆的)XSD:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="ArrayOfMyObject"
    targetNamespace="http://www.myproduct.com/2011/04/08/ArrayOfMyObject.xsd"
    xmlns="http://www.myproduct.com/2011/04/08/ArrayOfMyObject.xsd"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
    <xs:complexType name="MyObject">
        <xs:all minOccurs="0" maxOccurs="1">
            <xs:element name="CD" type="xs:int" />
            <xs:element name="CI" type="xs:string" />
            <xs:element name="CT" type="xs:int" />
            <xs:element name="CY" type="xs:int" />
            <xs:element name="LD" type="xs:int" />
            <xs:element name="SomeName" type="xs:string" />
            <xs:element name="SomeNumber" type="xs:string" />
            <xs:element name="SD" type="xs:int" />
            <xs:element name="ZP" type="xs:string" />
            <xs:element name="State">
                <xs:simpleType>
                    <xs:restriction base="xs:string">
                        <xs:pattern value="[A-Z][A-Z]" />
                    </xs:restriction>
                </xs:simpleType>
            </xs:element>
        </xs:all>
    </xs:complexType>

    <xs:element name="ArrayOfMyObject">
        <xs:complexType>
            <xs:sequence minOccurs="0" maxOccurs="unbounded">
                <xs:element name="MyObject" type="MyObject" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

我在 SQL Server 中创建了架构集合:

CREATE XML SCHEMA COLLECTION [dbo].[MySchemaCollection] AS
'<The XSD from above>'

(在这里进行明显的替换)。请注意,我做了

CREATE XML SCHEMA COLLECTION [dbo].[MySchemaCollection] AS
N'<The XSD from above which now gives an error>'

因为NVARCHAR 类型与 utf-8 的组合会导致 SQL Server 产生如下错误:

消息 9402,第 16 级,状态 1,第 3 行
XML解析:第1行,第39个字符,无法切换编码

现在,我重新创建表,相关列是:

[MyXmlColumn] [xml](DOCUMENT [dbo].[MySchemaCollection]) NULL

我希望我插入的是DOCUMENT,但我也尝试将列创建为CONTENT,但无济于事。

我将XmlSerializer 创建为

_xml = new XmlSerializer(typeof(MyObject[]));

并使用

调用序列化程序
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("t", "http://www.myproduct.com/2011/04/08/ArrayOfMyObject.xsd");
_xml.Serialize(serializationStream, graph, ns);

当然,serializationStream 是输出流(在本例中为 MemoryStream),graph 的类型为 MyObject[]

现在,XmlSerializer 产生如下输出:

  <?xml version="1.0" ?> 
  <ArrayOfMyObject xmlns:t="http://www.myproduct.com/2011/04/08/ArrayOfMyObject.xsd">
  <MyObject>
  <LD>1</LD> 
  <SomeName>SOME CITY 04</SomeName> 
  <SomeNumber>04</SomeNumber> 
  </MyObject>
  <MyObject>
  <LD>1</LD> 
  <SomeName>SOME CITY 05</SomeName> 
  <SomeNumber>05</SomeNumber> 
  </MyObject>
  <MyObject>
  <LD>1</LD> 
  <SomeName>SOME CITY 07</SomeName> 
  <SomeNumber>07</SomeNumber> 
  </MyObject>
  <MyObject>
  <LD>18</LD> 
  <SomeName>BANKS-FREMONT</SomeName> 
  <SomeNumber>BF</SomeNumber> 
  </MyObject>
  <MyObject>
  <LD>18</LD> 
  <SomeName>BENNINGTON</SomeName> 
  <SomeNumber>B000</SomeNumber> 
  </MyObject>
  <MyObject>
  <LD>18</LD> 
  <SomeName>CENTER</SomeName> 
  <SomeNumber>CE</SomeNumber> 
  </MyObject>
  <MyObject>
  <LD>18</LD> 
  <SomeName>FRANKLINE MAXFIELD CITY</SomeName> 
  <SomeNumber>FR</SomeNumber> 
  </MyObject>
  <MyObject>
  <LD>18</LD> 
  <SomeName>FREDERIKA LEROY CITY OF</SomeName> 
  <SomeNumber>FD</SomeNumber> 
  </MyObject>
  <MyObject>
  <LD>18</LD> 
  <SomeName>HARLAN</SomeName> 
  <SomeNumber>HL</SomeNumber> 
  </MyObject>
  <MyObject>
  <CT>15</CT> 
  <SomeName>ATLANTIC 2 AND GROVE</SomeName> 
  <SomeNumber>AT2</SomeNumber> 
  </MyObject>
  <MyObject>
  <CT>15</CT> 
  <SomeName>BRIGHTON/MARNE/GROVE 1/P</SomeName> 
  <SomeNumber>MR</SomeNumber> 
  </MyObject>
  <MyObject>
  <CT>15</CT> 
  <SomeName>EDNA/NOBLE/PLEASANT/GRIS</SomeName> 
  <SomeNumber>GS</SomeNumber> 
  </MyObject>
  <MyObject>
  <CT>15</CT> 
  <SomeName>FRANKLIN/WIOTA/GRANT/ANI</SomeName> 
  <SomeNumber>AN</SomeNumber> 
  </MyObject>
  <MyObject>
  <CT>15</CT> 
  <SomeName>MASSENA AND CITY</SomeName> 
  <SomeNumber>MS</SomeNumber> 
  </MyObject>
  <MyObject>
  <CT>15</CT> 
  <SomeName>UNION &amp; CUMBERLAND</SomeName> 
  <SomeNumber>CU</SomeNumber> 
  </MyObject>
  <MyObject>
  <CD>3</CD> 
  <State>IA</State> 
  </MyObject>
  <MyObject>
  <CD>5</CD> 
  <State>IA</State> 
  </MyObject>
  <MyObject>
  <CT>1</CT> 
  <State>IA</State> 
  </MyObject>
  <MyObject>
  <CT>2</CT> 
  <State>IA</State> 
  </MyObject>
  <MyObject>
  <CT>5</CT> 
  <State>IA</State> 
  </MyObject>
  </ArrayOfMyObject>

当我调用Context.SaveChanges 时,我得到一个异常,其内部异常消息是:

XML 验证:未找到元素“ArrayOfMyObject”的声明。位置:/*:ArrayOfMyObject[1]

我尝试使用许多不同的命名空间选项调用和/或创建序列化程序,但我得到相同或类似的错误(声称 {http://www.myproduct.com/2011/04/08/ArrayOfMyObject.xsd}ArrayOfMyObject 是预期的,但找到了 ArrayOfMyObject,或者相反反之亦然)。

我需要进行哪些更改才能使来自序列化程序的 XML 在 SQL Server 2008 上通过 XML 验证?

【问题讨论】:

    标签: sql-server-2008 .net-4.0 entity-framework-4 xsd xmlserializer


    【解决方案1】:

    我继续发布了这个问题,希望这个答案可以在以后对某人有所帮助,即使我实际上是在尝试从 XML 验证中重现“类似”错误消息之一时找到了答案。

    可能还有其他方法可以让它发挥作用,但最终对我有用的是:

  • 通过将xmlns 更改为xmlns:t 并添加elementFormDefault="qualified" 来更改XSD 中的xs:schema 元素以获得:
    <xs:schema id="ArrayOfMyObject"
        targetNamespace="http://www.myproduct.com/2011/04/08/ArrayOfMyObject.xsd"
        xmlns:t="http://www.myproduct.com/2011/04/08/ArrayOfMyObject.xsd"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        elementFormDefault="qualified">
    

  • minOccurs="0" 添加到MyObject 类型中的每个xs:element 标记。我误解了this reference document 的意思是使用xs:allminOccurs="0" 会自动应用于其中的所有xs:element 标签。如果这应该是正确的,SQL Server 似乎不会这样识别它。因此,为了通过验证,我必须使每个元素类似于
    <xs:element name="CD" type="xs:int" minOccurs="0" />
    

    因为并非所有标签都会出现在每个 MyObject 元素中。

  • ArrayOfMyObject 元素中将命名空间前缀t 添加到MyObject 的类型中:
    <xs:element name="ArrayOfMyObject">
        <xs:complexType>
            <xs:sequence minOccurs="0" maxOccurs="unbounded">
                <xs:element name="MyObject" type="t:MyObject" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    

  • 在创建我的XmlSerializer 时,同时使用类型和默认命名空间,用于序列化和反序列化:
    _xml = new XmlSerializer(typeof(MyObject[]), "http://www.myproduct.com/2011/04/08/ArrayOfMyObject.xsd");
    

    我之前曾尝试过,但现在我知道这是最终解决方案的一部分。

  • 调用XmlSerializer.Serialize时,请执行以下操作:
    XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
    ns.Add("t", "http://www.myproduct.com/2011/04/08/ArrayOfMyObject.xsd");
    _xml.Serialize(serializationStream, graph, ns);
    

  • MyObject 类本身上,添加属性
    [XmlRoot(Namespace="http://www.myproduct.com/2011/04/08/ArrayOfMyObject.xsd")]
    

  • 在序列化中包含的MyObject的每个属性上,添加属性
    [XmlElement(Namespace="http://www.myproduct.com/2011/04/08/ArrayOfMyObject.xsd", Form=System.Xml.Schema.XmlSchemaForm.Qualified)]
    

  • 最后,MyObject 已经使用ISerializable 实现了自定义序列化,并且在我启动此功能时,IXmlSerializable。这个事实可能会使上面的属性添加变得不必要,但我没有测试过这个断言。无论哪种方式,都必须在 IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) 方法中使用
    writer.WriteElementString(name, "http://www.myproduct.com/2011/04/08/ArrayOfMyObject.xsd", propertyValue.ToString());
    

    其中name 是要序列化的属性的名称,propertyValue 是它的值。 不必使用指定命名空间前缀的WriteElementString 形式;序列化程序自动包含正确的前缀。 IXmlSerializable.ReadXml(System.Xml.XmlReader reader) 方法已使用 reader.LocalName 检索元素名称,因此无需在此处进行任何更改来处理命名空间。

  • 对项目符号格式感到抱歉;列表似乎不能很好地与代码块搭配使用。

    我希望这对 Google 搜索中的某个人有所帮助。

    【讨论】:

      猜你喜欢
      • 2011-04-10
      • 1970-01-01
      • 2011-12-21
      • 1970-01-01
      • 1970-01-01
      • 2012-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多