派生 class 的 DataContractSerializer 命名空间与属性中指定的不匹配

DataContractSerializer namespace for derived class does not match specified in attribute

我期望 XML 输出如下:

<MyBase type="MyDerived"
        xmlns="http://www.mynamespace.com/MySchema" />

相反,我的实际输出如下:

<MyBase i:type="MyDerived"
        xmlns="http://www.mynamespace.com/MySchema"
        xmlns:i="http://www.w3.org/2001/XMLSchema-instance" />

我正在使用以下 class 定义来尝试生成我预期的输出:

MyBase.cs

namespace MyProject
{
    [KnownType(typeof(MyDerived))]
    [DataContract(Namespace = MyBase.Namespace)]
    public abstract class MyBase 
    {
        public const string Namespace = "http://www.mynamespace.com/MySchema";
    }
}

MyDerived.cs

namespace MyProject.Events
{
    [DataContract(Namespace = MyBase.Namespace)]
    public sealed class MyDerived : MyBase {}
}

我正在使用以下序列化代码:

var knownTypes = new Type[]
{
    typeof(MyDerived)
};

var xmlDictionary = new XmlDictionary(1);
var settings = new DataContractSerializerSettings();
settings.KnownTypes = knownTypes;
settings.RootNamespace = xmlDictionary.Add(MyBase.Namespace);

serializer = new DataContractSerializer(typeof(MyBase), settings);

var actual = String.Empty;

using (var memoryStream = new MemoryStream())
{
    serializer.WriteObject(memoryStream, new MyDerived());

    memoryStream.Position = 0;

    using (var streamReader = new StreamReader(memoryStream))
    {
        actual = streamReader.ReadToEnd();
    }
}

我不确定为什么它对我的派生对象使用 XMLSchema-instance 命名空间而不是我指定使用的命名空间。我花了一个多小时在 Whosebug、Google 和 MSDN 上挖掘,试图找出我做错了什么,但我一定是错过了它。看起来很接近,这一定是一个简单的错误。

这是我的 class 结构的问题,还是我以某种方式误用了属性?

如何获得预期的输出?

您可能只是误读了 XML 并且一切正常。在您的 XML 中,命名空间 xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 仅用于限定 属性 i:type,除此之外别无其他。您的所有实际数据都在您指定的命名空间中。如果对象本身位于不同的命名空间中,您会看到类似以下内容:

i:type="i:MyDerived"

但你不是。

http://www.w3.org/2001/XMLSchema-instance is a W3C globally standard namespace, knowledge of which is built in to DataContractSerializer (as well as many other XML serializers). It contains 4 built-in attributes defined as follows in the standards document:

  • nil:如果一个元素具有值为 true.

    [=59= 的元素,则表明该元素在没有内容的情况下可能是·有效·的]
  • schemaLocationnoNamespaceSchemaLocation:用于提供有关架构文档物理位置的提示。

  • type:实例中的元素信息项可以使用属性 type 显式断言其类型。该属性的值为·QName·。

您看到的 i:type 是这些全球公认的标准属性中的最后一个。它说:"this element has the following type"。 DataContractSerializer 使用它来表示 .Net 类型信息的原因可能包括:

  1. 是标准的。例如XmlSerializer recognizes and supports the same attribute.

  2. 您的元素可能有自己的名为 type 的数据属性。如果是这样,它将驻留在自己的命名空间中,not http://www.w3.org/2001/XMLSchema-instance。后者是为架构信息而不是内容保留的约定,从而避免了名称冲突。

有关更多信息,请参阅 Understanding Known Types and Data Contract Known Types

不幸的是,即使使用自定义 XmlWriter,您也无法实现它。 DataContractSerializer 不像 XMLSerializer 那样工作。添加到您的 xml 中的信息是为了支持以下事实:

The line xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" tells the XML parser that this document should be validated against a schema.

这些命名空间也被视为保留命名空间。因此,如果您尝试覆盖它们,.Net 运行时将抛出异常。

@dbc 解释得很好,包含命名空间是一个非常标准的过程的一部分,对您的 xml.

无害

如果你真的需要摆脱这个默认的命名空间,那么你只需要 hack 你的 XML 输出与字符串替换方法。但这可能会导致您在脱盐时遇到问题。