2009-11-16 4 views
7

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

<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? Я что-то делаю неправильно или должен делать что-то еще?

ответ

8

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

Но; это должно быть 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

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

  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

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

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

More info on XmlSerializerNamespaces

Schemas reference

[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

Добавить «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(); 
    }