2009-11-16 24 views
7

Sto utilizzando un DataContractSerializer per serializzare un oggetto in XML. L'oggetto principale è SecurityHolding con lo spazio dei nomi "http://personaltrading.test.com/" e contiene una proprietà chiamata Amount che è una classe con lo spazio dei nomi "http://core.test.com". Quando serializzo questo in XML ottengo il seguente:DataContractSerializer con più spazi dei nomi

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

Esiste comunque il controllo del prefisso d3p1? Sto facendo qualcosa di sbagliato o dovrei fare qualcos'altro?

risposta

8

In primo luogo, la scelta dell'alias di namespace non dovrebbe fare alcuna differenza con un parser ben formato.

Ma; deve essere DataContractSerializer? Con XmlSerializer, è possibile utilizzare il sovraccarico di Serialize che accetta uno XmlSerializerNamespaces. Questo ti permette di scegliere e scegliere i namespace e gli alias che usi.

In definitiva; DataContractSerializer è non destinato a fornire un controllo xml completo; questo non è il suo scopo. Se si desidera un rigoroso controllo xml, XmlSerializer è una scelta migliore, anche se è più vecchia (e presenta alcune sfumature/debolezze proprie).

Esempio completo:

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); 
    } 
} 

uscita:

<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

Ho risolto questo problema leggermente diverso a Marc che può essere implementata in una classe base.

  1. Creare un nuovo attributo per definire gli spazi dei nomi XML aggiuntivi che verranno utilizzati nel contratto dati.

    [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. Aggiungere l'attributo ai contratti di dati.

    [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. Poi nel metodo Save, l'uso di riflessione per ottenere gli attributi e poi scrivere sul file.

    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

ho lottato con anche questo problema. La soluzione che presento di seguito non è ottimale, ma funziona. Come Marc Gravell sopra, suggerisco di usare XmlSerializer.

Il trucco consiste nell'aggiungere un campo alla classe che restituisce un oggetto XmlSerializerNamespaces. Questo campo deve essere decorato con un attributo XmlNamespaceDeclarations. Nel costruttore della classe, aggiungi gli spazi dei nomi come mostrato nell'esempio seguente. Nel file xml in basso si noti che l'elemento radice è prefissato correttamente così come l'elemento 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

Add "http: //www.w3.org/2001/XMLSchema "namespace di:

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(); 
    }