#c# #xml #serialization
#c# #xml #сериализация
Вопрос:
Я пытаюсь сериализовать объект в строку.
XML, из которого были взяты модели c #, имел несколько пространств имен:
xmlns="http://www.example.org/standards/def/1"
xmlns:ac="http://www.example.org/Standards/xyz/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rlc="http://www.example.org/standards/def/1"
xmlns:def1="http://www.lol.com/Standards/lol.xsd" Version="2013-06" xsi:schemaLocation="http://www.lol.org/standards/def/1 lol.xsd"
Я сериализую его с:
var deserialize = (MyType)pageDeserializer.Deserialize(reader);
var namespaces = new XmlSerializerNamespaces();
namespaces.Add("ac", "urn:http://www.example.org/Standards/xyz/1");
namespaces.Add("rlc", "urn:http://www.example.org/standards/def/1");
namespaces.Add("def1", "http://www.lol.com/Standards/lol.xsd" Version="2013-06" xsi:schemaLocation="http://www.lol.org/standards/def/1 lol.xsd");
var str = pageDeserializer.SerializeAsUtf8<JvInsReinsurance>(deserialize, namespaces);
Где метод SerializeAsUtf8 является:
public static string SerializeAsUtf8<T>(this XmlSerializer serializer, T o, XmlSerializerNamespaces ns)
{
using (var textWriter = new Utf8StringWriter())
{
serializer.Serialize(textWriter, o, ns);
return textWriter.ToString();
}
}
Я ожидал, что мой XML будет выглядеть как:
<rlc:element1 attribute1="value">
<ac:element1>VALUR</ac:element1>
</rlc:element1>
Что я получаю, так это:
<element1 attribute1="value">
<element1>VALUR</element1>
</element1>
Но информация для пространства имен не включена, и это приводит к сбою последующей проверки xsd. Как я могу включить префиксы пространства имен?
ОБНОВЛЕНИЕ 1
Удаление urn, как предложено в комментариях, заставило меня пройти мимо первого шага. Теперь я получаю ошибку при проверке на соответствие XSD.
Я получаю следующие ошибки:
1.
The element 'ElementX' in namespace 'urn:http://www.example.org/standards/def/1' has invalid child element 'ElementY' in namespace 'http://www.example.org/standards/def/1'.
2.
The element 'ElementP' in namespace 'urn:http://www.example.org/standards/def/1' has invalid child element 'ElementQ' in namespace 'http://www.example.org/standards/def/1'.
Для 1. классы являются
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.example.org/standards/def/1")]
public partial class ElementX
{
[XmlElement("ElementYName")]
public ElementY[] ElementYNames { get; set; }
}
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.example.org/standards/def/1")]
public partial class ElementY
{
[XmlAttribute]
public string Field1 { get; set; }
public ElementYFieldAmountType FieldAmount { get; set; }
public string Field2 { get; set; }
private string field3;
/// <remarks/>
public string Field3
{
get
{
return this.field3;
}
set
{
this.field3 = value;
}
}
}
[Serializable]
[DesignerCategory("code")]
[XmlType(AnonymousType = true, Namespace = "http://www.example.org/standards/def/1")]
public class ElementYFieldAmountType
{
public FieldAmount Amt { get; set; }
}
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.example.org/standards/def/1")]
public class FieldAmount
{
private string _ccyField;
private decimal valueField;
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string Ccy
{
get
{
return this._ccyField;
}
set
{
this._ccyField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlTextAttribute()]
public decimal Value
{
get
{
return this.valueField;
}
set
{
this.valueField = value;
}
}
}
С помощью XSD
<xs:complexType name="ElementX">
<xs:sequence>
<xs:element ref="ElementY" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="ElementY" type="ElementYType"/>
<xs:element name="FieldAmount" type="AnyAmtType"/>
<xs:complexType name="ElementYType">
<xs:sequence>
<xs:element ref="Field2" minOccurs="0"/>
<xs:element ref="FieldAmount" minOccurs="0"/>
<xs:element ref="Field3" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="Field1" type="xs:NMTOKEN" use="required"/>
</xs:complexType>
Для 2
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.example.org/standards/def/1")]
public partial class ElementP
{
public ElementQ ElementQName { get; set; }
}
[Serializable]
[DesignerCategory("code")]
[XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.example.org/standards/def/1")]
public class ElementQ
{
public PercentageRateType Rate { get; set; }
}
[Serializable]
[DesignerCategory("code")]
[XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.example.org/standards/def/1")]
public class PercentageRateType
{
[XmlAttribute]
public string RateUnit { get; set; }
[XmlText]
public decimal Value { get; set; }
}
По-моему, они выглядят нормально, что с ними не так?
Комментарии:
1. Почему вы используете urn: http:// вместо http:// ?
2. Хороший вопрос. Это вне моего контроля, мы используем существующие сообщения, приходящие с этим определением
3. Вы пробовали использовать
XmlNamespaceManager
? Вот небольшой пример4. Я бы предположил, что это работает так же, как параметр XmlSerializerNamespaces. Также, поправьте, если я ошибаюсь, но в примере код десериализует строку?
5. Как выглядит ваш класс? пространство имен для каждого класса должно быть в свойстве в квадратных скобках над классом.
Ответ №1:
Убедитесь, что ваша заглавная буква указана правильно. В некоторых случаях у вас есть «стандарты», а в других — «Standards». Смотрите код ниже :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
ElementX elementX = new ElementX()
{
ElementYNames = new ElementY[] {
new ElementY() {
FieldAmount = new ElementYFieldAmountType() {
Amt = new FieldAmount() {
Ccy = "VALUR",
Value = 123.456M
}
},
Field1 = "a",
Field2 = "b",
Field3 = "c"
}
}
};
XmlSerializer serializer = new XmlSerializer(typeof(ElementX));
var namespaces = new XmlSerializerNamespaces();
namespaces.Add("ac", "http://www.example.org/Standards/xyz/1");
namespaces.Add("rlc", "http://www.example.org/Standards/def/1");
namespaces.Add("def1", "http://www.lol.com/Standards/lol.xsd");
string xml = Test.SerializeAsUtf8<ElementX>(serializer, elementX, namespaces);
}
}
public static class Test
{
public static string SerializeAsUtf8<T>(this XmlSerializer serializer, T o, XmlSerializerNamespaces ns)
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
StringWriter writer = new StringWriter();
using (XmlWriter xWriter = XmlWriter.Create(writer, settings))
{
serializer.Serialize(xWriter, o, ns);
return writer.ToString();
}
}
}
[XmlRoot(Namespace = "http://www.example.org/Standards/def/1")]
public partial class ElementX
{
[XmlElement("ElementYName")]
public ElementY[] ElementYNames { get; set; }
}
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.example.org/Standards/xyz/1")]
public partial class ElementY
{
[XmlAttribute]
public string Field1 { get; set; }
public ElementYFieldAmountType FieldAmount { get; set; }
public string Field2 { get; set; }
private string field3;
/// <remarks/>
public string Field3
{
get
{
return this.field3;
}
set
{
this.field3 = value;
}
}
}
[Serializable]
[XmlRoot("code")]
[XmlType(AnonymousType = true, Namespace = "http://www.example.org/Standards/xyz/1")]
public class ElementYFieldAmountType
{
public FieldAmount Amt { get; set; }
}
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.example.org/Standards/xyz/1")]
public class FieldAmount
{
private string _ccyField;
private decimal valueField;
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string Ccy
{
get
{
return this._ccyField;
}
set
{
this._ccyField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlTextAttribute()]
public decimal Value
{
get
{
return this.valueField;
}
set
{
this.valueField = value;
}
}
}
}
Ответ №2:
Ваш xsd неполон. Я создал несколько новых Xsd-файлов из ваших классов, которые могли бы выглядеть следующим образом: 2:
Schema0.xsd:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="http://www.example.org/standards/def/1" />
<xs:element name="ElementP" nillable="true" xmlns:q1="http://www.example.org/standards/def/1" type="q1:ElementP" />
<xs:element name="ElementQ" nillable="true" xmlns:q2="http://www.example.org/standards/def/1" type="q2:ElementQ" />
<xs:element name="PercentageRateType" nillable="true" xmlns:q3="http://www.example.org/standards/def/1" type="q3:PercentageRateType" />
<xs:element name="ElementX" nillable="true" xmlns:q4="http://www.example.org/standards/def/1" type="q4:ElementX" />
<xs:element name="ElementY" nillable="true" xmlns:q5="http://www.example.org/standards/def/1" type="q5:ElementY" />
<xs:element name="ElementYFieldAmountType" nillable="true" xmlns:q6="http://www.example.org/standards/def/1" type="q6:ElementYFieldAmountType" />
<xs:element name="FieldAmount" nillable="true" xmlns:q7="http://www.example.org/standards/def/1" type="q7:FieldAmount" />
</xs:schema>
Schema1.xsd:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:tns="http://www.example.org/standards/def/1" elementFormDefault="qualified" targetNamespace="http://www.example.org/standards/def/1" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="ElementP">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="ElementQName">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Rate">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:decimal">
<xs:attribute name="RateUnit" type="xs:string" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ElementQ">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Rate">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:decimal">
<xs:attribute name="RateUnit" type="xs:string" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PercentageRateType">
<xs:simpleContent>
<xs:extension base="xs:decimal">
<xs:attribute name="RateUnit" type="xs:string" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="ElementX">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="ElementYName">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="FieldAmount">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Amt">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:decimal">
<xs:attribute name="Ccy" type="xs:string" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Field2" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="Field3" type="xs:string" />
</xs:sequence>
<xs:attribute name="Field1" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ElementY">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="FieldAmount">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Amt">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:decimal">
<xs:attribute name="Ccy" type="xs:string" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Field2" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="Field3" type="xs:string" />
</xs:sequence>
<xs:attribute name="Field1" type="xs:string" />
</xs:complexType>
<xs:complexType name="ElementYFieldAmountType">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Amt">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:decimal">
<xs:attribute name="Ccy" type="xs:string" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="FieldAmount">
<xs:simpleContent>
<xs:extension base="xs:decimal">
<xs:attribute name="Ccy" type="xs:string" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:schema>
Затем код:
using System;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Linq;
using System.Xml.Serialization;
namespace MaPiTest
{
public class Utf8StringWriter : StringWriter
{
public sealed override Encoding Encoding { get { return Encoding.UTF8; } }
}
class Program
{
public static string SerializeAsUtf8<T>(XmlSerializer serializer, T o, XmlSerializerNamespaces ns)
{
using (var textWriter = new Utf8StringWriter())
{
serializer.Serialize(textWriter, o, ns);
return textWriter.ToString();
}
}
static void Main(string[] args)
{
ElementX elementX = new ElementX()
{
ElementYNames = new ElementY[] {
new ElementY() {
FieldAmount = new ElementYFieldAmountType() {
Amt = new FieldAmount() {
Ccy = "VALUR",
Value = 123.456M
}
},
Field1 = "a",
Field2 = "b",
Field3 = "c"
}
}
};
// Serialize
XmlSerializer serializer = new XmlSerializer(typeof(ElementX));
var namespaces = new XmlSerializerNamespaces();
namespaces.Add("ac", "http://www.example.org/standards/xyz/1");
namespaces.Add("rlc", "http://www.example.org/standards/def/1");
namespaces.Add("def1", "http://www.lol.com/standards/lol.xsd");
var xml = SerializeAsUtf8(serializer, elementX, namespaces);
// Read into document.
var doc = XDocument.Parse(xml);
// Validate document with xsd.
var schemas = new XmlSchemaSet();
schemas.Add("", XmlReader.Create(new StringReader(File.ReadAllText("schema0.xsd"))));
schemas.Add("http://www.example.org/standards/def/1", XmlReader.Create(new StringReader(File.ReadAllText("schema1.xsd"))));
string error = null;
doc.Validate(schemas, (o, e) => Console.WriteLine(error = e.Message));
}
}
}
Результирующий xml, кстати, выглядит следующим образом:
<?xml version="1.0" encoding="utf-8" ?>
<ElementX xmlns:ac="http://www.example.org/standards/xyz/1" xmlns:def1="http://www.lol.com/standards/lol.xsd" xmlns:rlc="http://www.example.org/standards/def/1">
<rlc:ElementYName Field1="a">
<rlc:FieldAmount>
<rlc:Amt Ccy="VALUR">123.456</rlc:Amt>
</rlc:FieldAmount>
<rlc:Field2>b</rlc:Field2>
<rlc:Field3>c</rlc:Field3>
</rlc:ElementYName>
</ElementX>
Это подтверждает правильность.
Вы могли бы попытаться изменить xml, либо просто добавив изменение вручную, либо изменив имя свойства в ElementX. А затем проверьте ошибку проверки либо в консоли, либо в точке останова.
Надеюсь, это поможет…
Ответ №3:
Спасибо всем за ваши предложения!
В итоге ошибка была вызвана тем, что элементы были сериализованы не по порядку, а проверка xsd выдавала загадочное сообщение об ошибке
The element 'ElementX' in namespace 'urn:http://www.example.org/standards/def/1' has invalid child element 'ElementY' in namespace 'http://www.example.org/standards/def/1'.
Использование XmlOrder для упорядочивания атрибутов решило проблему
[XmlElement(Order = 1, IsNullable = true)]
public string ElementY