Почему мой XML не проверяется в типизированном столбце XML в SQL Server 2008? - PullRequest
1 голос
/ 14 апреля 2011

Супер-короткая версия:

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

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

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

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

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

У меня есть столбец XML в таблице SQL Server 2008. Если я просто использую нетипизированный столбец XML, вставка вывода XmlSerializer.Serialize работает нормально (я использую Entity Framework, который переводит столбец XML в тип 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>

Я создал коллекцию схем в 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 было найдено, или наоборот Versa).

Что мне нужно изменить, чтобы XML из сериализатора проходил проверку XML на SQL Server 2008?

1 Ответ

1 голос
/ 14 апреля 2011

Я опубликовал этот вопрос, надеясь, что ответ позже может кому-нибудь помочь, хотя я выяснил ответ, когда на самом деле пытался воспроизвести одно из «похожих» сообщений об ошибках из проверки 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 с 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>

При создании my 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.

...