C# 可以序列化属性的内部 setter 吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/420662/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can an internal setter of a property be serialized?
提问by Patrik Svensson
Is there any way to serialize a property with an internal setter in C#?
I understand that this might be problematic - but if there is a way - I would like to know.
有没有办法在 C# 中使用内部 setter 序列化属性?
我知道这可能有问题 - 但如果有办法 - 我想知道。
Example:
例子:
[Serializable]
public class Person
{
public int ID { get; internal set; }
public string Name { get; set; }
public int Age { get; set; }
}
Code that serializes an instance of the class Person:
序列化类Person的实例的代码:
Person person = new Person();
person.Age = 27;
person.Name = "Patrik";
person.ID = 1;
XmlSerializer serializer = new XmlSerializer(typeof(Person));
TextWriter writer = new StreamWriter(@"c:\test.xml");
serializer.Serialize(writer, person);
writer.Close();
Result (missing the ID property):
结果(缺少 ID 属性):
<?xml version="1.0" encoding="utf-8"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Patrik</Name>
<Age>27</Age>
</Person>
采纳答案by Marc Gravell
If it is an option, DataContractSerializer
(.NET 3.0) can serialize non-public properties:
如果它是一个选项,DataContractSerializer
(.NET 3.0) 可以序列化非公共属性:
[DataContract]
public class Person
{
[DataMember]
public int ID { get; internal set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}
...
static void Main()
{
Person person = new Person();
person.Age = 27;
person.Name = "Patrik";
person.ID = 1;
DataContractSerializer serializer = new DataContractSerializer(typeof(Person));
XmlWriter writer = XmlWriter.Create(@"c:\test.xml");
serializer.WriteObject(writer, person);
writer.Close();
}
With the xml (re-formatted):
使用 xml(重新格式化):
<?xml version="1.0" encoding="utf-8"?>
<Person xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/">
<Age>27</Age>
<ID>1</ID>
<Name>Patrik</Name>
</Person>
回答by Stormenet
I think that the only alternativeone way is to implement IXmlSerializableand do the object xml writing / parsing yourself.
我认为唯一的替代方法是实现IXmlSerializable并自己编写/解析对象 xml。
Edit:After reading the comments, DataContractSerializerlooks interesting ;)
编辑:阅读评论后,DataContractSerializer看起来很有趣;)
回答by Kent Boogaart
If you're doing "default" XML serialization it will only look at public properties. Implementing IXmlSerializable
will get you control over exactly what is serialized. If you're doing "standard" .NET serialization, it will look at fields - not properties, so your object will still be correctly serialized without any need for implementing any extra interfaces.
如果您正在进行“默认”XML 序列化,它只会查看公共属性。实施IXmlSerializable
将使您准确控制序列化的内容。如果您正在执行“标准”.NET 序列化,它将查看字段 - 而不是属性,因此您的对象仍将被正确序列化,而无需实现任何额外的接口。
回答by Rowland Shaw
Not that I've found without doing some work. I believe this is because the XmlSerializer
that is generated uses reflection to generate a new class (which is in a new assembly, so cannot see internal
member/methods).
并不是说我没有做一些工作就发现了。我相信这是因为XmlSerializer
生成的那个使用反射来生成一个新类(它在一个新的程序集中,所以看不到internal
成员/方法)。
There may be some mileage in using an XmlSerialization PreCompilierto generate the code, and then to modify it into an internal class for your purposes, so you'd then do something like:
使用XmlSerialization PreCompilier生成代码,然后根据您的目的将其修改为内部类可能会有一些进展,因此您可以执行以下操作:
XmlSerializer serializer = new MyPersonXmlSerializer();
The other option (and probably preferable) is to implement IXmlSerializablewhich will guide the auto-generated code to do the right thing.
另一种选择(可能更可取)是实现IXmlSerializable,它将引导自动生成的代码做正确的事情。
回答by Darin Dimitrov
You can implement IXmlSerializable, unfortunately this negates the most important benefit of XmlSerializer (the ability to declaratively control serialization). DataContractSerializer(xml based) and BinaryFormatter(binary based) could be used as alternatives to XmlSerializer each having its pros and cons.
您可以实现IXmlSerializable,不幸的是,这否定了 XmlSerializer 最重要的好处(以声明方式控制序列化的能力)。DataContractSerializer(基于 xml)和BinaryFormatter(基于二进制)可以用作 XmlSerializer 的替代品,每个都有其优点和缺点。
回答by Alexandru
It certainly is possible. I want to illustrate a solution using XElement
, one that I've grown quite fond of by the way. You do not need to use the XmlSerializer
or the DataContractSerializer
or any class or property annotations like [DataContract]
or [Serializable]
for that matter, if you do not wish to do so. Also, the example below shows how You can interchange private set
with internal set
in my example, by the way:
这当然是可能的。我想用 来说明一个解决方案XElement
,顺便说一下,我非常喜欢这个解决方案。如果您不想这样做,则不需要使用XmlSerializer
或DataContractSerializer
或 任何类或属性注释,例如[DataContract]
或[Serializable]
就此而言。另外,如下图的例子,如何可以互换private set
使用internal set
在我的例子,顺便说一下:
using System;
using System.Linq;
using System.Xml.Linq;
namespace SerializationTesting
{
class Person
{
// Notice how this object type uses private setters, something that the traditional XmlSerializer will complain about if you don't use a wrapper class..
public string Name { get; private set; }
public DateTime Birthday { get; private set; }
public long HeightInMillimeters { get; private set; }
public Gender Gendrality { get; private set; }
// Generate a serialized XElement from this Person object.
public XElement ToXElement()
{
return new XElement("person",
new XAttribute("name", Name),
new XAttribute("birthday", Birthday),
new XAttribute("heightInMillimeters", HeightInMillimeters),
new XAttribute("gendrality", (long)Gendrality)
);
}
// Serialize this Person object to an XElement.
public static Person FromXElement(XElement x)
{
return new Person(
(string)x.Attribute("name"),
(DateTime)x.Attribute("birthday"),
(long)x.Attribute("heightInMillimeters"),
(Gender)(long)x.Attribute("gendrality")
);
}
public Person(string name, DateTime birthday, long heightInMillimeters, Gender gender)
{
Name = name;
Birthday = birthday;
HeightInMillimeters = heightInMillimeters;
Gendrality = gender;
}
// You must override this in conjunction with overriding GetHashCode (below) if you want .NET collections (HashSet, List, etc.) to properly compare Person objects.
public override bool Equals(object obj)
{
if (obj.GetType() == typeof(Person))
{
Person objAsPerson = (Person)obj;
return Name == objAsPerson.Name && Birthday == objAsPerson.Birthday && HeightInMillimeters == objAsPerson.HeightInMillimeters && Gendrality == objAsPerson.Gendrality;
}
return false;
}
// You must override this in conjunction with overriding Equals (above) if you want .NET collections (HashSet, List, etc.) to properly compare Person objects.
public override int GetHashCode()
{
return Name.GetHashCode() ^ Birthday.GetHashCode() ^ HeightInMillimeters.GetHashCode() ^ Gendrality.GetHashCode();
}
// This allows us to compare Person objects using the == operator.
public static bool operator ==(Person a, Person b)
{
return a.Equals(b);
}
// This allows us to compate Person objects using the != operator.
public static bool operator !=(Person a, Person b)
{
return !a.Equals(b);
}
}
public enum Gender
{
Male,
Female
}
class Program
{
static void Main(string[] args)
{
// Create first person (note how UTC time saves and loads properly when casting).
Person personOne = new Person("Alexandru", DateTime.UtcNow, 1000, Gender.Male);
// Save the first person to a local file on the hard disk.
personOne.ToXElement().Save("PersonOne.dat");
// Create second person (not using UTC time this time around).
Person personTwo = new Person("Alexandria", DateTime.Now, 900, Gender.Female);
// Save the second person to a local file on the hard disk.
personTwo.ToXElement().Save("PersonTwo.dat");
// Load the first person from a local file on the hard disk.
XDocument personOneDocument = XDocument.Load("PersonOne.dat");
Person personOneLoadedFromDocument = Person.FromXElement(personOneDocument.Elements().First());
// Load the second person from a local file on the hard disk.
XDocument personTwoDocument = XDocument.Load("PersonTwo.dat");
Person personTwoLoadedFromDocument = Person.FromXElement(personTwoDocument.Elements().First());
// Serialize the first person to a string and then load them from that string.
string personOneString = personOne.ToXElement().ToString();
XDocument personOneDocumentFromString = XDocument.Parse(personOneString);
Person personOneLoadedFromDocumentFromString = Person.FromXElement(personOneDocumentFromString.Elements().First());
// Check for equalities between persons (all outputs will be "true").
Console.WriteLine(personOne.Equals(personOneLoadedFromDocument));
Console.WriteLine(personTwo.Equals(personTwoLoadedFromDocument));
Console.WriteLine(personOne == personOneLoadedFromDocument);
Console.WriteLine(personTwo == personTwoLoadedFromDocument);
Console.WriteLine(personOne != personTwo);
Console.WriteLine(personOneLoadedFromDocument != personTwoLoadedFromDocument);
Console.WriteLine(personOne.Equals(personOneLoadedFromDocumentFromString));
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
}
The output of all of the equality checks in the console application above will be true
, as expected. This does not suffer from annoyances like having to keep track of encodings or the way you parse data because it does all of that for you, and it does not limit your class to public setters like the XmlSerializer
does.
true
正如预期的那样,上面控制台应用程序中所有相等性检查的输出都是。这不会受到诸如必须跟踪编码或解析数据的方式之类的烦恼,因为它为您完成了所有这些工作,并且它不会像这样将您的类限制为公共设置XmlSerializer
器。