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

时间:2015-10-28 19:48:24

标签: c# xml datacontractserializer

我期待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" />

我使用以下类定义来尝试生成我期望的输出:

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命名空间而不是我指定使用的命名空间。我花了一个多小时在StackOverflow,谷歌和MSDN上挖掘,试图弄清楚我做错了什么,但我必须错过它。它看起来如此接近,一定是一个简单的错误。

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

如何获得预期的输出?

2 个答案:

答案 0 :(得分:2)

您可能只是误读了XML并且事情正在按预期工作。在您的XML中,命名空间xmlns:i="http://www.w3.org/2001/XMLSchema-instance"仅用于限定属性 i:type,而不是其他任何内容。您的所有实际数据都在您指定的命名空间中。如果对象本身位于不同的命名空间中,您会看到如下内容:

i:type="i:MyDerived"

但你不是。

http://www.w3.org/2001/XMLSchema-instance是一个W3C全局标准命名空间,其知识内置于DataContractSerializer(以及许多其他XML序列化程序)。它包含4个内置属性,在standards document中定义如下:

  • nil:如果元素的值为true,则表示元素可能有效·没有内容。

  • schemaLocationnoNamespaceSchemaLocation:用于提供有关架构文档的物理位置的提示。

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

您看到的i:type是这些标准的,全球公认的属性中的最后一个。它说:“这个元素有以下类型”。 DataContractSerializer使用它来表示.Net类型信息的原因可能包括:

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

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

  3. 有关详情,请参阅Understanding Known TypesData Contract Known Types

答案 1 :(得分:1)

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

  

xmlns:xsi =“http://www.w3.org/2001/XMLSchema-instance”告诉XML解析器应该根据模式验证此文档。

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

@dbc解释得非常好,包含命名空间是一个非常标准的过程的一部分,对你的xml无害。

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