【问题标题】:Why is XmlSerializer so hard to use?为什么 XmlSerializer 这么难用?
【发布时间】:2010-03-23 15:46:09
【问题描述】:

我想像这样使用 XML 序列化:

class Foo {
    public Foo (string name) {
        Name1 = name;
        Name2 = name;
    }

    [XmlInclude]
    public string Name1 { get; private set; }

    [XmlInclude]
    private string Name2;
}

StreamWriter wr = new StreamWriter("path.xml");
new XmlSerializer<Foo>().Serialize (wr, new Foo ("me"));

编辑:我知道这段代码是错误的。只是为了显示我想如何使用它。

但这根本不起作用:

  • XmlSerializer 不是通用的。我必须从和反对(反)序列化。
  • 每个属性都必须完全公开。为什么我们不只是使用反射来访问私有设置器?
  • 不能序列化私有字段。我想用一个属性装饰私有字段,让 XmlSerializer 包含它们。

我是否遗漏了什么,而 XmlSerializer 实际上提供了所描述的可能性?是否有替代的 XML 序列化程序可以更复杂地处理这些情况?

如果不是:我们毕竟是在 2010 年,而 .NET 已经存在很多年了。 XML 序列化经常被使用,完全标准并且应该很容易执行。还是我的理解可能是错误的,并且 XML 序列化不应该有充分的理由公开所描述的功能?

编辑: 遗留不是 imo 的一个好理由。 List一开始也不是通用的。

(请随意调整标题或标签。如果这应该是 CW,请留言。)

【问题讨论】:

  • 同一个道理,用篮球挖洞是如此困难。 ;-)
  • 说“List 起初是非通用的”是不对的。 List 是在 2.0 中引入的,当时是一个泛型类。较旧的非泛型ArrayList 是一个不同的 类,它仍然存在
  • 哎呀。我的错误。我道歉:)

标签: c# xmlserializer


【解决方案1】:

XmlSerializer class。你会发现你用错了。 XmlInclude 的目的完全不同。

你是对的。 XML 序列化器从 .NET 1.0 开始就已经存在。那是在我们有泛型之前,顺便说一句,所以不太可能支持它们。

此外,从那时起,已经出现了更好的技术:

  • DataContractSerializer 速度更快,支持二进制序列化
  • LINQ to XML 可用于多种序列化场景,并且更加灵活

XML 序列化器在未来不太可能得到增强。我建议您学习其他替代方法。

【讨论】:

  • @Marc: DataContractSerializerXmlDictionaryWriter.CreateBinaryWriter 不写二进制?
  • 还不错;我会收回的。
【解决方案2】:

首先是固定代码,然后是问题的答案:

public class Foo {
    public Foo() : this("") {}
    public Foo (string name) {
        Name1 = name;
        Name2 = name;
    }
    // note only this will be serialized
    public string Name1 { get; private set; }
    // this won't
    private string Name2;
}

或在 3.0 中:

[DataContract]
class Foo {
    public Foo (string name) {
        Name1 = name;
        Name2 = name;
    }
    [DataMember]
    public string Name1 { get; private set; }
    [DataMember]
    private string Name2;
}

(并使用DataContractSerializer 而不是XmlSerializer

XmlSerializer 不是通用的。我必须从和反对(反)序列化。

这对于序列化程序很常见。我有自己的序列化程序,最初我确实 使它完全通用。结果证明这是一个很大的设计错误。巨大的。不,认真的。我目前正在重写每一行代码以将其切换出去。

简单;序列化程序通常涉及某种程度的反射(对于代码生成或实际工作,取决于实现)。反射和泛型不能很好地发挥作用,尤其是在 WCF 等一些框架上。让你的代码做最后的演员是一个公平的妥协。如果你真的想要的话,我有 number 篇关于此的博客文章...

每个属性都必须完全公开。

这确实是XmlSerializer 的限制(尽管列表/集合没有 setter 很好,但如果您有公共 get 和私有 set,它抛出)。此外,该类型需要是公共的并且有一个无参数的构造函数。

我们为什么不直接使用反射来访问私有设置器?

为了提高性能,XmlSerializer 动态构建程序集以执行您想要的操作。它无法自动访问您的代码内部。有关信息,我正在做类似的事情,但我提供 2 个级别的生成;完全静态的(到可部署的 dll 中),然后仅适用于公共成员或内存中,可以仍然访问私有成员。我猜他们只想选择一个模型,这是有道理的 - 他们需要“sgen”,它决定了第一个模型。

不能序列化私有字段。我想用一个属性装饰私有字段,让 XmlSerializer 包含它们。

然后使用DataContractSerializer,它将序列化任何标记为[DataMember]的成员(包括私有成员)。

【讨论】:

  • 你的博客在我的待办事项列表中;)很好的解释!
  • 我认为“固定”代码示例不太正确(令人惊讶的是,鉴于此答案已经出现在这里)。特别是,XmlSerializer 要求,据我所知(并且您的答案文本似乎证实)getter 和 setter 都是公开的。如果使用 XmlSerializer,则不会序列化带有私有 setter 的公共属性。
【解决方案3】:

1:传统。 XML 序列化器早于泛型。与 .NET 1.0 一样。

2:设计决策。与其他解决方案相比,XML 序列化程序应该使用非常有限的权限。

3:与 2 相同。

您可以部分使用 WCF DataContract 序列化程序。

您的假设是“有限的错误”。 XML 序列化据说是用于传输文档,在我的项目中,它总是单独的类,什么也不做。因此,我对所有限制都没有问题。

【讨论】:

    【解决方案4】:

    你不需要 [Xml包含] 就像你拥有它一样。您可以使用 [Xml元素] [Xml属性] ... 描述类的序列化方式。

    取出 [XmlInclude] 看看是否有效。

    class Foo { 
        public Foo (string name) { 
            Name1 = name; 
            Name2 = name; 
        } 
    
        [XmlAttribute] 
        public string Name1 { get; set; } 
    
        [XmlAttribute] 
        public string Name2; 
    } 
    
    Foo myFoo = new Foo("FirstName", "LastName");
    StreamWriter wr = new StreamWriter("path.xml"); 
    XmlSerializer serializer = new XmlSerializer(typeof(Foo));
    serializer.Serialize(wr,  myFoo);
    

    更新,序列化的属性应该是公开的。

    【讨论】:

    • 如果我没记错的话,只能序列化公共属性。
    • 好吧,它不起作用,因为相应的设置器是私有的。 -- 编辑:我很慢 :)
    • 你说得对,我只是剪切粘贴了他的内容,现在已经更新了。
    • 是的,但是出于 OOP 设计的原因,我想将它们保密。
    【解决方案5】:

    顺便说一句,DataContract 从不支持二进制序列化,它序列化为 xml 但支持二进制编码。

    【讨论】:

    • @Kerem:带有​​ XmlDictionaryWriter.CreateBinaryWriter 的 DataContractSerializer 写入二进制文件。你说它不支持“二进制序列化”是什么意思?
    猜你喜欢
    • 1970-01-01
    • 2022-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-13
    • 2019-03-05
    • 1970-01-01
    相关资源
    最近更新 更多