Почему мой XML не проверяется в типизированном столбце XML в SQL Server 2008?

#sql-server-2008 #.net-4.0 #entity-framework-4 #xsd #xmlserializer

#sql-server-2008 #.net-4.0 #entity-framework-4 #xsd #xmlserializer

Вопрос:

Сверхкороткая версия:

Я решил эту проблему, когда почти закончил писать вопрос. Ответ скоро будет.

Краткая версия:

Почему SQL Server отклоняет мой XML, вставленный в типизированный столбец XML? XML генерируется с использованием System.Xml.Serialization.XmlSerializer в .NET 4.

Гораздо более длинная версия:

У меня есть класс, который я могу успешно сериализовать, используя XmlSerializer . Более конкретно, я могу успешно сериализовать массив этого класса с помощью XmlSerializer .

У меня есть столбец XML в таблице SQL Server 2008. Если я просто использую нетипизированный XML-столбец, вставка выходных данных XmlSerializer.Serialize работает нормально (я использую Entity Framework, которая преобразует XML-столбец в type string ). Однако вместо этого я хотел бы сделать столбец типизированным столбцом XML. Когда я это делаю, что бы я ни пытался, я, похоже, не могу получить XML из сериализатора для успешной проверки.

Вот (слегка запутанный) XSD, который я использовал для создания типизированного столбца XML в SQL Server:

 <?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>
  

Я создал коллекцию 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
  

I would expect what I am inserting to be a DOCUMENT , but I have also tried creating the column as CONTENT to no avail.

Я создаю 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 находится выходной поток (в данном случае, a 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;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 из сериализатора прошел проверку XML на SQL Server 2008?

Ответ №1:

Я пошел дальше и опубликовал этот вопрос, надеясь, что ответ позже сможет кому-нибудь помочь, хотя я выяснил ответ, когда на самом деле пытался воспроизвести одно из «похожих» сообщений об ошибках при проверке XML.

Могут быть другие способы заставить это работать, но в итоге у меня сработало:

  • Измените xs:schema элемент в XSD, изменив xmlns на xmlns:t и добавив elementFormDefault="qualified" , чтобы получить:

     <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" к каждому из xs:element тегов в MyObject типе. Я неправильно понял, что этот справочный документ означает, что использование xs:all with minOccurs="0" автоматически применяется ко всем xs:element тегам внутри. Если предполагается, что это правильно, SQL Server, похоже, не распознает его как таковой. Итак, чтобы пройти проверку, мне пришлось сделать каждый элемент похожим на

     <xs:element name="CD" type="xs:int" minOccurs="0" />
      

    поскольку не все теги будут присутствовать в каждом MyObject элементе.

  • Добавьте префикс пространства имен t к типу MyObject внутри ArrayOfMyObject элемента:

     <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.