有没有办法在C#中用内部setter序列化一个属性?
我知道这可能会有问题 - 但如果有办法 - 我想知道。
示例:
[Serializable]
public class Person
{
public int ID { get; internal set; }
public string Name { get; set; }
public int Age { get; set; }
}
序列化类 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();
结果(缺少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>
答案 0 :(得分:54)
如果是一个选项,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();
}
使用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>
答案 1 :(得分:5)
你可以实现IXmlSerializable,遗憾的是这否定了XmlSerializer最重要的好处(声明性地控制序列化的能力)。 DataContractSerializer(基于xml)和BinaryFormatter(基于二进制)可以用作XmlSerializer的替代品,每个都有其优缺点。
答案 2 :(得分:4)
我认为唯一的替代方法是实现IXmlSerializable并自己做对象xml编写/解析。
修改:阅读评论后,DataContractSerializer看起来很有趣;)
答案 3 :(得分:1)
如果您正在进行“默认”XML序列化,那么它只会查看公共属性。实现IXmlSerializable
将使您可以准确控制序列化的内容。如果您正在进行“标准”.NET序列化,它将查看字段 - 而不是属性,因此您的对象仍然可以正确序列化,而无需实现任何额外的接口。
答案 4 :(得分:1)
并非我没有做过一些工作就找到了。我相信这是因为生成的XmlSerializer
使用反射生成一个新类(在新程序集中,因此无法看到internal
成员/方法)。
使用XmlSerialization PreCompilier生成代码,然后根据您的目的将其修改为内部类可能会有一些里程,所以您可以执行以下操作:
XmlSerializer serializer = new MyPersonXmlSerializer();
另一个选项(可能更可取)是实现IXmlSerializable,它将指导自动生成的代码做正确的事情。
答案 5 :(得分:0)
当然有可能。我想用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();
}
}
}
上述控制台应用程序中所有相等性检查的输出将为true
,如预期的那样。这不会因为必须跟踪编码或解析数据的方式而烦恼,因为它会为您完成所有这些,并且它不会将您的类限制为XmlSerializer
之类的公共设置者。