很难在 .NET 中可用的 XML 序列化选项之间做出选择
直到昨晚,我一直在使用 .NET 中的各种库来解析 XML——主要是 XmlDocument
和 XDocument
。我不知道为什么我没有早点研究这个问题,但我想到 .NET 中一定有一些可用的东西可以免费为您提供类序列化/反序列化,当然,它以 < 的形式出现代码>XmlSerializer类。
果然,通过使用几行代码,我能够轻松地序列化和反序列化(尽管在我当前编写的代码中,我只需要反序列化),并且不再需要花几个小时左右编写我自己的类来与其他库一起执行此操作,以及必要的单元测试。但问题是我希望我的属性是只读的。如果我将 setter 设置为私有,那么在创建 XmlSerializer 时,我会收到此错误:
无法生成临时类(结果=1)。 错误 CS0200:无法分配属性或索引器“MyProperty”——它是只读的
它看起来像 这是一个无法解决的问题,因此必须有一个解决方法。
无法找到元素“MyProperty”的架构信息。
这是因为没有代码为 MyProperty 赋值,因为 XmlSerializer 不知道如何处理私有字段!
我在 StackOverflow 上找到了一个答案,它提出了另一种解决方案,即使用 DataContractSerializer
我以前没听说过。我对我的类进行了必要的代码更改,但最终得到了与上面相同的消息。我运行了代码以确保,当 XML 反序列化时,类成员不会被设置。
我认为在我的特殊情况下,我要么接受它并允许成员被覆盖(坏),要么回到我原来的做事方式,即只编写所有序列化/反序列化代码我。我在这里做错了什么,或者是否不可能允许像 XmlSerializer 这样的类在反序列化期间设置类的私有成员,同时使该类的使用者无法覆盖其成员?
更新:和另一篇文章展示了对私有属性进行反序列化的另一种方法,但我刚刚尝试过,但它也不起作用。
以下是我尝试反序列化的类的一些示例:
[Export]
[DataContract]
public class Configuration
{
[DataMember(Name="Port")]
private int _port;
public int Port { get { return _port; }}
}
结果:使用 XmlSerializer 反序列化时,没有错误,但当我的 XML 文件的 Port 值为 1 时,_port 和 Port 的值为 0,例如 <代码><端口>1。
另一个例子:
[Export]
public class Configuration
{
public int Port { get; set; }
}
结果:反序列化很好,但我不想要公共设置器。
我像这样反序列化该类:
XmlSerializer serializer = new XmlSerializer(typeof(Configuration));
FileStream reader = new FileStream( "config.xml", FileMode.Open);
Configuration Config = (Configuration)serializer.Deserialize( reader);
reader.Close();
Until last night, I've been parsing XML using a variety of libraries in .NET -- XmlDocument
and XDocument
mostly. I'm not sure why I didn't look into this sooner, but it occurred to me that there must be something available in .NET that gives you class serialization / deserialization for free, and of course that comes in the form of the XmlSerializer
class.
Sure enough, by using a few lines of code, I was able to serialize and deserialize with ease (although in the code I'm currently writing, I only need to deserialize), and no longer had to take the few hours or so to write my own class to do this with other libraries, plus the requisite unit tests. But the problem is that I would like my properties to be read-only. If I make the setter private, then upon creation of the XmlSerializer I get this error:
Unable to generate a temporary class (result=1).
error CS0200: Property or indexer 'MyProperty' cannot be assigned to -- it is read only
It looks like this is an issue that won't be resolved, so there must be a workaround.
Sure enough, I found this information, which indicates that you can get the code to compile if you give up auto properties and just back with private fields. Unfortunately, while this compiles, when you execute the code, it doesn't actually deserialize the data. After stopping my application, I noticed several entries in the Messages window that said this:
Could not find schema information for the element 'MyProperty'.
And this is because there's no code to assign a value to MyProperty, because XmlSerializer doesn't know how to deal with private fields!!!
I found an answer on StackOverflow that presents another solution, which is to use a DataContractSerializer
which I hadn't heard of before. I made the necesary code changes to my class, but ended up with the same messages as above. I ran the code to be sure, and the class members don't get set when the XML is deserialized.
I'm thinking that in my particular case, I either suck it up and allow the members to get overwritten (bad), or I go back to my original way of doing things, which is to just write all of the serialization / deserialization code myself. Is there something I'm doing wrong here, or is it impossible to allow a class like XmlSerializer to set private members of a class during deserialization, while making the consumer of the class not be able to overwrite its members?
UPDATE: and yet another article that shows another way to do deserialization of private properties, but I just tried it and it also doesn't work.
Here are some examples of the class that I've tried to deserialize:
[Export]
[DataContract]
public class Configuration
{
[DataMember(Name="Port")]
private int _port;
public int Port { get { return _port; }}
}
Result: when deserializing with the XmlSerializer, there are no errors, but _port and Port have a value of 0 when my XML file has a Port value of 1, e.g. <Port>1</Port>
.
Another example:
[Export]
public class Configuration
{
public int Port { get; set; }
}
Result: deserializes fine, but I don't want a public setter.
I deserialize the class like this:
XmlSerializer serializer = new XmlSerializer(typeof(Configuration));
FileStream reader = new FileStream( "config.xml", FileMode.Open);
Configuration Config = (Configuration)serializer.Deserialize( reader);
reader.Close();
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我没有一个好的答案,但我有几个不好的答案。排名不分先后:
您可以创建具有读/写属性的纯数据传输对象,并将其用于序列化/反序列化。然后,您可以通过使用 DTO 进行构造来初始化不可变业务对象。
您提到的两个序列化器类还允许通过属性和代码的组合覆盖它们的行为。这可能足以解决您的问题,但工作量很可能超过其价值。
I don't have a good answer, but I have several bad ones. In no particular order:
You can create a pure data transfer object with r/w properties and use it for serialization/deserialization. You can then initialize your immutable business object by constructing with the DTO.
The two serializer classes you mentioned also allow overriding their behavior through a combination of attributes and code. This is likely sufficient to solve your problem, but may well be more work than it's worth.
这不是使用哪种 XML 序列化方法的问题,而是关于序列化本身的问题(尝试使用二进制序列化,您将得到相同的错误)。
如果无法更改属性以允许使用默认序列化方法,则:
现在,这可能看起来有点像回到手动序列化,但是它只需要在问题中的类上完成,并且它还使您的类可以通过其他机制进行序列化。
This isn't a question of which XML serialisation method to use, but about the serialisation itself (try it with a binary serialisation, and you'll get the same error).
If you cannot change the property to allow the default serialisation method to take place, then:
Now, this may seem a bit like going back to manual serialisation, however it need only be done on the classes in quesiton, and it also makes your class serialisable by other mechanisms.
我认为,最终,这个问题的正确答案是,如果您需要反序列化(序列化不是问题)私有数据,那么您需要避免使用 XmlSerializer 和 DataContractSerializer。我仍然希望我是错的,但最终,乔恩可能是对的——我需要使用 ISerialized,或者按照我迄今为止所做的方式进行操作。
今晚,当我决定使用一个具有公共属性的类来允许反序列化,然后用另一个类包装它时,我遇到了另一堵墙。我不想将可反序列化的类公开——我希望它成为仅公开只读属性的私有类。问题是 XmlSerialized 无法处理私有类。
我现在可以将类公开,我可能这样做只是为了让事情首先工作,但我将走不同的路线,因为必须公开该类是没有意义的。
I think, ultimately, that the correct answer to this question is that if you need to deserialize (serialization isn't an issue) private data, then you want to avoid using XmlSerializer and DataContractSerializer. I still hope that I'm wrong, but in the end, Jon might be right -- I will need to either use ISerializable, or do it the way I've done it up until now.
I hit another brick wall tonight when I decided to use one class with public properties to allow deserialization, and then wrap it with another class. I didn't want to make the deserializable class public -- I wanted it to be a private class within the one that only exposes readonly properties. Well, the issue with that is that XmlSerializable can't deal with classes that are private.
I could just make the class public for now, which I might do just to get things working first, but I'm going to go a different route because having to make that class public just doesn't make sense.