【问题标题】:DataContractSerializer with Multiple Namespaces具有多个命名空间的 DataContractSerializer
【发布时间】:2009-11-16 18:55:06
【问题描述】:

我正在使用 DataContractSerializer 将对象序列化为 XML。主要对象是具有命名空间“http://personaltrading.test.com/”的 SecurityHolding,并包含一个名为 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 前缀吗?我做错了什么还是应该做其他事情?

【问题讨论】:

    标签: c# xml namespaces datacontractserializer


    【解决方案1】:

    首先,命名空间别名的选择对格式良好的解析器应该没有影响。

    但是;必须是DataContractSerializer 吗?使用XmlSerializer,您可以使用接受XmlSerializerNamespacesSerialize 的重载。这允许您挑选和选择您使用的命名空间和别名。

    最终; 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>
    

    【讨论】:

    • “命名空间别名的选择对格式良好的解析器应该没有影响”。如果可以的话,请与大型德国 ERP 软件制造商分享信息,以便他们停止制作像 &lt;ns0:Something xmlns:ns0='muhnamespace'&gt;... 这样的奇怪 xml,我会欠你的。
    【解决方案2】:

    我解决这个问题的方式与 Marc 略有不同,可以在基类中实现。

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

    【讨论】:

      【解决方案3】:

      我也一直在努力解决这个问题。我在下面提出的解决方案不是最佳的恕我直言,但它有效。像上面的 Marc Gravell 一样,我建议使用 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" />
      

      【讨论】:

        【解决方案4】:

        通过以下方式添加“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();
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-09-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-08-15
          • 2017-04-29
          • 2013-06-03
          • 2020-01-03
          相关资源
          最近更新 更多