【问题标题】:Predefine XML namespaces for DataContractSerializer为 DataContractSerializer 预定义 XML 命名空间
【发布时间】:2012-06-28 14:35:14
【问题描述】:

我正在构建一个自托管的 WCF 服务。我正在为非常灵活的数据传输构建一个特殊的数据结构。到目前为止,我使用 DataContractSerializer 测试我的结构是否可序列化。效果很好,我对此很高兴,但有一些让我烦恼的事情:

在我的 XML 输出中有许多重新定义的 xmlns 属性,例如:

xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"
xmlns:b="http://www.w3.org/2001/XMLSchema"

这应该在根元素中更好地定义一次,以便可以简单地优化字节。有没有办法将自定义命名空间信息添加到根元素?

这里有一个更大的例子来说明我的意思:

<DataObject xmlns="http://schemas.datacontract.org/2004/07/Test"
            xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Data xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <a:KeyValueOfstringanyType>
      <a:Key>ID</a:Key>
      <a:Value i:type="b:int" xmlns:b="http://www.w3.org/2001/XMLSchema">1</a:Value>
    </a:KeyValueOfstringanyType>
    <a:KeyValueOfstringanyType>
      <a:Key>Value</a:Key>
      <a:Value i:type="b:int" xmlns:b="http://www.w3.org/2001/XMLSchema">42</a:Value>
    </a:KeyValueOfstringanyType>
  </Data>
  <Data xmlns:a="...">...</Data>
  <Data xmlns:a="...">...</Data>
  <Data xmlns:a="...">...</Data>
</DataObject>

我想要的是这样的:

<DataObject xmlns="http://schemas.datacontract.org/2004/07/Test"
            xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"
            xmlns:b="http://www.w3.org/2001/XMLSchema">
  <Data>
    <a:KeyValueOfstringanyType>
      <a:Key>ID</a:Key>
      <a:Value i:type="b:int">1</a:Value>
    </a:KeyValueOfstringanyType>
    <a:KeyValueOfstringanyType>
      <a:Key>Value</a:Key>
      <a:Value i:type="b:int">42</a:Value>
    </a:KeyValueOfstringanyType>
  </Data>
  <Data>...</Data>
  <Data>...</Data>
  <Data>...</Data>
</DataObject>

【问题讨论】:

标签: c# xml-serialization xml-namespaces datacontractserializer


【解决方案1】:
static void Main()
{
    var o = new Foo {
        Prop = new Dictionary<string,string> { {"foo","bar"} }
    };

    var ms = new MemoryStream();

    var slz = new DataContractSerializer(typeof(Foo));
    slz.WriteObject(ms, o,
        new Dictionary<string,string>
        {
            { "arr", "http://schemas.microsoft.com/2003/10/Serialization/Arrays" },
        });

    string data = Encoding.UTF8.GetString(ms.ToArray());
    Console.WriteLine(data);
}

public static class Extensions
{
    public static void WriteObject(
        this DataContractSerializer serializer,
        Stream stream, object data,
        Dictionary<string,string> namespaces)
    {
        using (var writer = XmlWriter.Create(stream))
        {
            serializer.WriteStartObject(writer, data);
            foreach (var pair in namespaces)
            {
                writer.WriteAttributeString("xmlns", pair.Key, null, pair.Value);
            }
            serializer.WriteObjectContent(writer, data);
            serializer.WriteEndObject(writer);
        }
    }
}

[DataContract]
class Foo
{
    [DataMember]
    public Dictionary<string,string> Prop;
}

输出:

<?xml version="1.0" encoding="utf-8"?>
<Foo xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays"
     xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
     xmlns="http://schemas.datacontract.org/2004/07/">
    <Prop>
        <arr:KeyValueOfstringstring>
            <arr:Key>foo</arr:Key>
            <arr:Value>bar</arr:Value>
        </arr:KeyValueOfstringstring>
    </Prop>
</Foo>

【讨论】:

  • 看起来很完美。只是遗憾的是没有属性为我添加了那种 declamation。
  • 这似乎是一个合适的解决方案。再次感谢你!使用DataContractSerializer 一切正常。但是DataContractJsonSerializer 抛出了一个关于DataContractResolver 或者我应该使用KnownTypeAttribute 的SerializationException。为什么DataContractJsonSerializer 想要,而DataContractSerializer 不想要?
  • Extensions中的扩展方法只适用于DataContractSerializer
  • 我知道。我只是在测试我的结构是否也可以以 JSON 格式序列化。 (不使用您的代码)
  • 您可以在Extensions 中为DataContractJsonSerializer 添加一个虚拟扩展方法。
【解决方案2】:

我成功使用了这里描述的解决方案:http://blogs.msdn.com/b/youssefm/archive/2009/07/24/optimizing-away-repeat-xml-namespace-declarations-with-datacontractserializer.aspx

您基本上创建了一个为您将命名空间添加到根元素的行为。

来自文章:

只需创建一个从 XmlObjectSerializer 继承的序列化程序,它的所有方法都使用 DataContractSerializer,除了它在顶层注册额外的命名空间。然后使用返回您刚刚创建的XmlObjectSerializerCreateSerializer 方法创建从DataContractSerializerOperationBehavior 派生的行为并插入该行为。

如果您想在 Silverlight 中执行此操作,也可以使用此处描述的解决方案:http://blogs.msdn.com/b/carlosfigueira/archive/2011/05/24/wcf-extensibility-custom-serialization-in-silverlight.aspx

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-12
    • 1970-01-01
    相关资源
    最近更新 更多