DataContractSerializer с несколькими пространствами имен - PullRequest
7 голосов
/ 16 ноября 2009

Я использую DataContractSerializer для сериализации объекта в XML. Основным объектом является SecurityHolding с пространством имен "http://personaltrading.test.com/" и содержит свойство с именем Amount, которое является классом с пространством имен" http://core.test.com". Когда я сериализую это в XML, я получаю следующее:

<ArrayOfSecurityHolding xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://personaltrading.test.com/">
  <SecurityHolding>
    <Amount xmlns:d3p1="http://core.test.com/">
        <d3p1:Amount>1.05</d3p1:Amount>
        <d3p1:CurrencyCode>USD</d3p1:CurrencyCode>
    </Amount>
    <BrokerageID>0</BrokerageID>
    <BrokerageName i:nil="true" />
    <RecordID>3681</RecordID>
  </SecurityHolding></ArrayOfSecurityHolding>

Могу ли я в любом случае контролировать префикс d3p1? Я делаю что-то не так или я должен делать что-то еще?

Ответы [ 4 ]

8 голосов
/ 16 ноября 2009

Во-первых, выбор псевдонима пространства имен не должен иметь никакого значения для правильно сформированного синтаксического анализатора.

Но; это должно быть DataContractSerializer? С XmlSerializer вы можете использовать перегрузку Serialize, которая принимает XmlSerializerNamespaces. Это позволяет вам выбирать, какие пространства имен и псевдонимы вы используете.

В конечном счете; DataContractSerializer - это , а не , предназначенный для полного контроля над XML; это не его цель. Если вам нужен строгий контроль xml, XmlSerializer будет лучшим выбором, даже если он старше (и имеет свои собственные нюансы / недостатки).

Полный пример:

using System;
using System.Xml.Serialization;
public class Amount
{
    public const string CoreNamespace = "http://core.test.com/";
    [XmlElement("Amount", Namespace=CoreNamespace)]
    public decimal Value { get; set; }
    [XmlElement("CurrencyCode", Namespace = CoreNamespace)]
    public string Currency { get; set; }
}
[XmlType("SecurityHolding", Namespace = SecurityHolding.TradingNamespace)]
public class SecurityHolding
{
    public const string TradingNamespace = "http://personaltrading.test.com/";

    [XmlElement("Amount", Namespace = Amount.CoreNamespace)]
    public Amount Amount { get; set; }

    public int BrokerageId { get; set; }
    public string BrokerageName { get; set; }
    public int RecordId { get; set; }
}
static class Program
{
    static void Main()
    {
        var data = new[] {
            new SecurityHolding {
                Amount = new Amount {
                    Value = 1.05M,
                    Currency = "USD"
                },
                BrokerageId = 0,
                BrokerageName = null,
                RecordId = 3681
            }
        };
        var ser = new XmlSerializer(data.GetType(),
            new XmlRootAttribute("ArrayOfSecurityHolding") { Namespace = SecurityHolding.TradingNamespace});
        var ns = new XmlSerializerNamespaces();
        ns.Add("foo", Amount.CoreNamespace);
        ser.Serialize(Console.Out, data, ns);
    }
}

Выход:

<ArrayOfSecurityHolding xmlns:foo="http://core.test.com/" xmlns="http://personaltrading.test.com/">
  <SecurityHolding>
    <foo:Amount>
      <foo:Amount>1.05</foo:Amount>
      <foo:CurrencyCode>USD</foo:CurrencyCode>
    </foo:Amount>
    <BrokerageId>0</BrokerageId>
    <RecordId>3681</RecordId>
  </SecurityHolding>
</ArrayOfSecurityHolding>
4 голосов
/ 04 июля 2011

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

  1. Создайте новый атрибут для определения дополнительных пространств имен XML, которые вы будете использовать в своем контракте данных.

    [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]    
    public sealed class NamespaceAttribute : Attribute    
    {   
    
        public NamespaceAttribute()
        {
        }
    
        public NamespaceAttribute(string prefix, string uri)
        {
            Prefix = prefix;
            Uri = uri;
        }
    
        public string Prefix { get; set; }
        public string Uri { get; set; }
    }
    
  2. Добавьте атрибут в ваши контракты данных.

    [DataContract(Name = "SomeObject", Namespace = "http://schemas.domain.com/namespace/")]    
    [Namespace(Prefix = "a", Uri = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]    
    [Namespace(Prefix = "wm", Uri = "http://schemas.datacontract.org/2004/07/System.Windows.Media")]           
    public class SomeObject : SerializableObject          
    {    
    
        private IList<Color> colors;
    
        [DataMember]
        [DisplayName("Colors")]
        public IList<Colors> Colors
        {
            get { return colors; }
            set { colours = value; }
        }
    }
    
  3. Затем в вашем методе Save используйте отражение, чтобы получить атрибуты, а затем запишите их в файл.

    public static void Save(SerializableObject o, string filename)
    {
        using (Stream outputStream = new FileStream(filename, FileMode.Create, FileAccess.Write))
        {
            if (outputStream == null)
                throw new ArgumentNullException("Must have valid output stream");
    
            if (outputStream.CanWrite == false)
                throw new ArgumentException("Cannot write to output stream");
    
            object[] attributes;
            attributes = o.GetType().GetCustomAttributes(typeof(NamespaceAttribute), true);    
    
            XmlWriterSettings writerSettings = new XmlWriterSettings();                
            writerSettings.Indent = true;
            writerSettings.NewLineOnAttributes = true;                
            using (XmlWriter w = XmlWriter.Create(outputStream, writerSettings))
            {
                DataContractSerializer s = new DataContractSerializer(o.GetType());
    
                s.WriteStartObject(w, o);
                foreach (NamespaceAttribute ns in attributes)                      
                    w.WriteAttributeString("xmlns", ns.Prefix, null, ns.Uri);
    
                // content
                s.WriteObjectContent(w, o);
                s.WriteEndObject(w);
            }
        }
    }
    
1 голос
/ 18 сентября 2012

Я тоже боролся с этой проблемой. Решение, которое я представляю ниже, не является оптимальным ИМХО, но оно работает. Как и Марк Гравелл выше, я предлагаю использовать XmlSerializer.

Хитрость заключается в том, чтобы добавить в ваш класс поле, которое возвращает объект XmlSerializerNamespaces. Это поле должно быть украшено атрибутом XmlNamespaceDeclarations. В конструкторе вашего класса добавьте пространства имен, как показано в примере ниже. В приведенном ниже XML-коде обратите внимание, что к корневому элементу добавлен правильный префикс, а также к элементу someString.

Дополнительная информация о XmlSerializerNamespaces

Ссылка на схему

[XmlRoot(Namespace="http://STPMonitor.myDomain.com")]
public class CFMessage : IQueueMessage<CFQueueItem>
{
    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces xmlns;

    [XmlAttribute("schemaLocation", Namespace=System.Xml.Schema.XmlSchema.InstanceNamespace)]
    public string schemaLocation = "http://STPMonitor.myDomain.com/schemas/CFMessage.xsd";

    [XmlAttribute("type")]
    public string Type { get; set; }

    [XmlAttribute("username")]
    public string UserName { get; set; }

    [XmlAttribute("somestring", Namespace = "http://someURI.com")]
    public string SomeString = "Hello World";


    public List<CFQueueItem> QueueItems { get; set; }

    public CFMessage()
    {
        xmlns = new XmlSerializerNamespaces();
        xmlns.Add("myDomain", "http://STPMonitor.myDomain.com");
        xmlns.Add("xyz", "http://someURI.com");
    }
}


<?xml version="1.0" encoding="utf-16"?>
<myDomain:CFMessage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xyz="http://someURI.com"
xsi:schemaLocation="http://STPMonitor.myDomain.com/schemas/CFMessage.xsd"
xyz:somestring="Hello World" type="JOIN" username="SJ-3-3008-1"
xmlns:myDomain="http://STPMonitor.myDomain.com" />
0 голосов
/ 15 января 2013

Добавить "http://www.w3.org/2001/XMLSchema" пространство имен по:

    private static string DataContractSerialize(object obj)
    {
        StringWriter sw = new StringWriter();

        DataContractSerializer serializer = new DataContractSerializer(obj.GetType());

        using (XmlTextWriter xw = new XmlTextWriter(sw))
        {
            //serializer.WriteObject(xw, obj);
            //
            // Insert namespace for C# types
            serializer.WriteStartObject(xw, obj);
            xw.WriteAttributeString("xmlns", "x", null, "http://www.w3.org/2001/XMLSchema");
            serializer.WriteObjectContent(xw, obj);
            serializer.WriteEndObject(xw);
        }

        StringBuilder buffer = sw.GetStringBuilder();

        return buffer.ToString();
    }
...