简体   繁体   English

具有多个命名空间的DataContractSerializer

[英]DataContractSerializer with Multiple Namespaces

I am using a DataContractSerializer to serialize an object to XML. 我正在使用DataContractSerializer将对象序列化为XML。 The main object is SecurityHolding with the namespace " http://personaltrading.test.com/ " and contains a property called Amount that's a class with the namespace " http://core.test.com ". 主要对象是带有命名空间“ http://personaltrading.test.com/ ”的SecurityHolding,它包含一个名为Amount的属性,它是一个名为“ http://core.test.com ”的类。 When I serialize this to XML I get the following: 当我将其序列化为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>

Is there anyway I can control the d3p1 prefix? 反正我可以控制d3p1前缀吗? Am I doing something wrong or should I be doing something else? 我做错了什么或者我应该做别的事吗?

Firstly, the choice of namespace alias should make no difference to a well-formed parser. 首先,命名空间别名的选择应该与格式良好的解析器没有区别。

But; 但; does it have to be DataContractSerializer ? 它必须是DataContractSerializer吗? With XmlSerializer , you can use the overload of Serialize that accepts a XmlSerializerNamespaces . 使用XmlSerializer ,您可以使用接受XmlSerializerNamespacesSerialize的重载。 This allows you to pick and choose the namespaces and aliases that you use. 这允许您选择和使用您使用的命名空间和别名。

Ultimately; 最终, DataContractSerializer is not intended to give full xml control; DataContractSerializer 并非旨在提供完整的xml控件; that isn't its aim. 这不是它的目标。 If you want strict xml control, XmlSerializer is a better choice, even if it is older (and has some nuances/foibles of its own). 如果你想要严格的xml控件, XmlSerializer是一个更好的选择,即使它更老(并且有自己的一些细微差别/弱点)。

Full example: 完整示例:

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

Output: 输出:

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

I have solved this problem slightly differently to Marc that can be implemented in a base class. 我已经解决了这个问题与Marc略有不同,可以在基类中实现。

  1. Create a new attribute to define the additional XML namespaces that you will use in your data contract. 创建新属性以定义将在数据协定中使用的其他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. Add the attribute to your data contracts. 将属性添加到数据协定中。

     [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. Then in your Save method, use reflection to get the attributes and then write them to the file. 然后在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); } } } 

I have struggled with this problem also. 我也在努力解决这个问题。 The solution I present below is not optimal IMHO but it works. 我在下面提出的解决方案不是最佳恕我直言,但它的工作原理 Like Marc Gravell above, I suggest using XmlSerializer. 和上面的Marc Gravell一样,我建议使用XmlSerializer。

The trick is to add a field to your class that returns a XmlSerializerNamespaces object. 诀窍是在类中添加一个返回XmlSerializerNamespaces对象的字段。 This field must be decorated with a XmlNamespaceDeclarations attribute. 必须使用XmlNamespaceDeclarations属性修饰此字段。 In the constructor of your class, add namespaces as shown in the example below. 在类的构造函数中,添加名称空间,如下面的示例所示。 In the xml below note that the root element is prefixed correctly as well as the someString element. 在下面的xml中注意,根元素以及someString元素都是正确的前缀。

More info on XmlSerializerNamespaces 有关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" />

Add "http://www.w3.org/2001/XMLSchema" namespace by: 添加“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();
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM