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