可以序列化属性的内部setter吗?

时间:2009-01-07 15:10:57

标签: c# .net serialization

有没有办法在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>

6 个答案:

答案 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说明一个解决方案,顺便说一句,我已经非常喜欢了这个解决方案。如果您不希望这样做,则无需使用XmlSerializerDataContractSerializer或任何类别或属性注释,例如[DataContract][Serializable]。此外,下面的示例显示了如何在我的示例中将private setinternal 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之类的公共设置者。