我正在编写代码以进行Xml序列化。具有以下功能。

public static string SerializeToXml(object obj)
{
    XmlSerializer serializer = new XmlSerializer(obj.GetType());
    using (StringWriter writer = new StringWriter())
    {
        serializer.Serialize(writer, obj);
        return writer.ToString();
    }
}


如果参数是没有无参数构造函数的类的实例,则会引发异常。


未处理的异常:
System.InvalidOperationException:
CSharpConsole.Foo无法序列化
,因为它没有
无参数构造函数。在
System.Xml.Serialization.TypeDesc.CheckSupported()

System.Xml.Serialization.TypeScope.GetTypeDesc(Type
类型,MemberInfo源,布尔值
直接引用,布尔throwOnError)在
System.Xml.Serialization.ModelScope.GetTypeModel(Type
类型,布尔直接引用)在
System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping (Type
类型,XmlRootAttribute根,String
defaultNamespace)位于
System.Xml.Serialization.XmlSerializer..ctor(Type
类型,String defaultName空间) System.Xml.Serialization.XmlSerializer..ctor(Type
type)


为什么必须有一个无参数构造函数才能使xml序列化成功?

编辑:感谢cfeduke的回答。无参数构造函数可以是私有的也可以是内部的。

评论

如果您有兴趣,我发现了无需构造函数即可创建对象的方法(请参阅更新)-但这完全不会帮助XmlSerializer-它仍然需要它。可能对自定义代码很有用。

XmlSerializer要求使用默认的无参数构造函数进行反序列化。

#1 楼

在对象的反序列化过程中,负责对对象进行反序列化的类将创建序列化类的实例,然后仅在获取要填充的实例后,才继续填充序列化的字段和属性。

只要需要,就可以将其构造为privateinternal

评论


哦,所以,我可以将无参数的ctor设为私有或内部,并且序列化仍然有效。感谢您的回答。

–程摩根
08年11月6日在5:39

是的,我经常这样做,尽管我已经接受了公共的无参数构造函数非常棒,因为它们允许您将“ new()”与泛型和新的初始化语法一起使用。对于带参数的构造函数,请使用静态工厂方法或构建器模式实现。

–cfeduke
08年11月6日在6:14

可访问性提示是一个很好的提示,但是您的解释对序列化没有任何意义。仅需要为反序列化创建对象。我可能会猜测类型检查代码内置在XmlSerializer构造函数中,因为单个实例可以同时使用两种方式。

–特默·加贝尔(Tomer Gabel)
09年1月18日在11:05

@jwg一个例子是,当您将XML发送到某种Web服务并且对接收自己组件中的那些对象不感兴趣时​​。

–特默·加贝尔(Tomer Gabel)
13年3月7日在9:49

请记住,即使将无参数的构造函数设为私有或内部构造,其值已序列化的所有属性也必须具有公共设置程序。

– chrnola
2014年6月10日20:09

#2 楼

这是XmlSerializer的限制。请注意,BinaryFormatterDataContractSerializer不需要这样做-它们可以从ether中创建未初始化的对象,并在反序列化期间对其进行初始化。

由于您使用的是xml,因此您可以考虑使用DataContractSerializer并用[DataContract] / [DataMember],但请注意,这会更改架构(例如,没有[XmlAttribute]的等效项-一切都变为元素)。

更新:如果您真的想知道,BinaryFormatter等人可以使用FormatterServices.GetUninitializedObject()创建对象而不调用构造函数。可能很危险;我不建议经常使用它;-p另请参见MSDN上的说明:


因为对象的新实例
被初始化为零且没有
运行构造函数,该对象可能不表示该对象认为其有效的状态。当用户希望
立即填充所有字段时,当前的
方法仅应用于
反序列化。它
不会创建未初始化的
字符串,因为创建一个不可变类型的空
实例没有作用。



我有我自己的序列化引擎,但我不打算使用FormatterServices;我很想知道一个构造函数(任何构造函数)实际上已经执行。

评论


感谢您提供有关FormatterServices.GetUninitializedObject(Type)的技巧。 :)

–奥马尔·范·克洛滕
09年2月4日在9:23

;原来我不听从自己的建议; protobuf-net允许(可选)年龄使用FormatterServices

– Marc Gravell♦
2012年9月24日上午8:39

但是我不明白的是,在没有指定构造函数的情况下,编译器会创建一个公共的无参数构造函数。那么,为什么对于xml反序列化引擎来说还不够好呢?

– toddmo
15年1月8日在15:31

如果我想反序列化XML并使用其构造函数初始化某些对象(以便通过构造函数提供元素/属性),是否有任何方法可以实现?是否没有办法自定义序列化过程,以便它使用其构造函数来构建对象?

– Shimmy Weitzhandler
17年11月17日在12:37

@Shimmy nope;不支持。有IXmlSerializable,但是a:发生在构造函数之后,b:非常丑陋,很难正确设置(尤其是反序列化)-我强烈建议您不要尝试实现它,但是:它将不允许您使用构造函数

– Marc Gravell♦
17年11月17日在13:29



#3 楼

答案是:毫无根据。

与其名称相反,XmlSerializer类不仅用于序列化,而且还用于反序列化。它对您的类执行某些检查以确保它可以正常工作,并且其中一些检查仅与反序列化相关,但是无论如何都会执行它们,因为它不知道您以后打算做什么。

您的课程未通过的检查只是与反序列化相关的检查之一。这是发生的情况:


反序列化期间,XmlSerializer类将需要创建您类型的
实例。
为了创建一个类型的实例,需要调用该类型的构造函数

如果您未声明构造函数,则编译器已经提供了
默认的无参数构造函数,但是,如果您声明了
构造函数,则这是唯一可用的构造函数。 ,如果您声明的构造函数接受参数,那么
实例化类的唯一方法是调用该接受参数的构造函数

但是,XmlSerializer无法调用任何构造函数
除了无参数的构造函数外,因为它不知道要传递给接受参数的构造函数的参数是什么。因此,它将检查您的类是否具有无参数构造函数,并且由于该构造函数没有无参数构造函数,因此它将失败。

因此,如果XmlSerializer类的编写方式仅执行检查与序列化相关,那么您的类将通过,因为关于序列化的任何事情都没有,因此没有必要使用无参数构造函数。

正如其他人已经指出的那样,对您的问题的快速解决方案是简单地添加无参数构造函数。不幸的是,这也是一个肮脏的解决方案,因为这意味着您不能从构造函数参数中初始化任何readonly成员。

除此之外,XmlSerializer类的编写方式还可以允许在没有无参数构造函数的情况下甚至反序列化类。它所要做的就是利用“工厂方法设计模式”(Wikipedia)。从外观上看,Microsoft认为对于DotNet程序员来说,这种设计模式太先进了,显然,不应将它们与这些东西混淆。因此,根据Microsoft的说法,DotNet程序员应该更好地坚持使用无参数构造函数。

评论


大声笑,你说,没有任何理由,然后继续说,XmlSerializer不能调用除无参数构造函数之外的任何构造函数,因为它不知道将什么参数传递给接受参数的构造函数。如果它不知道要传递给构造函数的参数,那么它将如何知道要传递给工厂的参数呢?还是使用哪个工厂?我无法想象该工具更简单易用-您想要反序列化一个类,然后让反序列化器创建一个默认实例,然后填充您标记的每个字段。简单。

–卡盘
5月12日18:12

@Chuck您可能想问另一个StackOverflow问题:“工厂方法如何通过构造函数参数帮助我”,让我知道,我将为您解答。

– Mike Nakis
8月3日10:29

#4 楼

首先,这是在文档中写的。我认为这是您的类领域之一,而不是主要领域-以及您如何希望反序列化器将其构造为无参数构造?

我认为有一种变通办法可以使构造函数私有。