【问题标题】:C# XML Serialization issue { get {...} }C# XML 序列化问题 { get {...} }
【发布时间】:2015-11-21 02:44:23
【问题描述】:

我已经阅读了很多其他关于 c# xml 序列化的问题,但我似乎无法解决我遇到的这个小小的理解问题。

我希望它不会太长,请考虑以下代码:

public class Grandparent {
    [XmlAttribute(AttributeName = "name")]
    public string Name { get; set; }
}



public class ParentA : Grandparent {
    private Grandparent _neighbor;

    [XmlAttribute(AttributeName = "neighbor")]
    public string NeighborName {  get { return _neighbor.Name; } }

    public ParentA() : this(null) { }
    public ParentA(Grandparent neighbor) {
        setNeighbor(neighbor);
    }

    public void setNeighbor(Grandparent neighbor) {
        // Do some checking
        _neighbor = neighbor;
    }
}



public class ParentB : Grandparent {
    private List<Grandparent> _people;

    [XmlElement(ElementName = "child1")]
    public List<ChildA1> Children1 {  get { return _people.Where(p => p.GetType() == typeof(ChildA1)).Cast<ChildA1>().ToList(); } }

    [XmlElement(ElementName = "child2")]
    public List<ChildA2> Children2 { get { return _people.Where(p => p.GetType() == typeof(ChildA2)).Cast<ChildA2>().ToList(); } }

    [XmlElement(ElementName = "parentc")]
    public List<ParentC> ParentsC { get { return _people.Where(p => p.GetType() == typeof(ParentC)).Cast<ParentC>().ToList(); } }

    public ParentB() {
        _people = new List<Grandparent>();
    }

    public void AddPerson(Grandparent person) {
        // Do some checking
        _people.Add(person);
    }
}



public class ParentC : Grandparent {
}



public class ChildA1 : ParentA {
}



public class ChildA2 : ParentA {
}



[XmlRoot("myroot")]
public class Model {
    private List<ParentB> _parents;

    [XmlElement(ElementName = "parentb")]
    public List<ParentB> Parents {  get { return _parents; } }

    public Model() {
        _parents = new List<ParentB>();
    }

    public void AddParent(ParentB parent) {
        // Do some checking
        _parents.Add(parent);
    }

    public string Serialize() {
        XmlSerializer serializer = new XmlSerializer(typeof(Model));

        using (StringWriter writer = new StringWriter()) {
            serializer.Serialize(writer, this);
            return writer.ToString();
        }
    }
}

现在让我们创建一个模型并用一些数据填充它:

Model model = new Model();

ChildA1 child1 = new ChildA1 { Name = "Alex" };
ChildA1 child2 = new ChildA1 { Name = "Ben" };
ChildA2 child3 = new ChildA2 { Name = "Jim" };
ChildA2 child4 = new ChildA2 { Name = "Pete" };

child1.setNeighbor(child2);
child2.setNeighbor(child1);
child3.setNeighbor(child4);
child4.setNeighbor(child3);

ParentB parent1 = new ParentB { Name = "Fred" };
ParentC parent2 = new ParentC { Name = "Sam" };

parent1.AddPerson(parent2);
parent1.AddPerson(child1);
parent1.AddPerson(child2);
parent1.AddPerson(child3);
parent1.AddPerson(child4);

model.AddParent(parent1);

textBox1.Text = model.Serialize();

这是我们得到的输出xml:

<?xml version="1.0" encoding="utf-16"?>
<myroot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <parentb name="Fred">
    <child1 name="Alex" />
    <child1 name="Ben" />
    <child2 name="Jim" />
    <child2 name="Pete" />
    <parentc name="Sam" />
  </parentb>
</myroot>

现在我们终于找到了我的问题的原因: 据我了解,只有公共数据会被序列化,这意味着方法(或从它们返回的数据)不会被序列化。 所以像public string Test { get; set; } 这样的东西可能会转化为一个公共领域......所以它可能会被序列化。 但是这个呢:public string Test { get { return _someString; } }? 这只是像public string get_Test() { return _someString; } 这样的普通方法的语法糖,不是吗?而且我们知道从方法返回的数据没有被序列化,对吗?

是的,有证据:

[XmlAttribute(AttributeName = "neighbor")]
public string NeighborName {  get { return _neighbor.Name; } }

这没有被序列化。 但是等等..什么?以下一项有效:

[XmlElement(ElementName = "child1")]
public List<ChildA1> Children1 {  get { return _people.Where(p => p.GetType() == typeof(ChildA1)).Cast<ChildA1>().ToList(); } }

我想我可以争辩说,像public string Test { get { return _someObj.someString; } } 这样的简单getter 只是访问一个数据字段,可能 转换为可以序列化的东西?但是上面发布的更复杂的 getter 确实有效,为什么简单版本(邻居)不起作用? 我一定是错过了什么……它盯着我的眼睛,但我就是看不见。

请帮我解开这个谜题。

PS:上面的类和代码只是对实际引发此问题的代码的重新构建。它的结构相似,产生的输出也非常相似。我确实很快在应用程序中实现和测试了这段代码,它似乎表明了我的问题,如果我在克隆原始代码的基本结构时犯了一些错误,请原谅我。

【问题讨论】:

  • 是否打算将一个用XmlAttribute 装饰,另一个用XmlElement 装饰?

标签: c# xml serialization


【解决方案1】:

只读属性不会序列化,因为它们无法在往返过程中反序列化回对象数据。

但是,对于返回集合的只读属性的规则有一个例外 -> IEnumerable

反序列化器在往返过程中(当 xml 再次成为类时)所做的是将元素作为类型添加到集合中。它永远不必“设置”属性。

注意:在你的情况下,如果你尝试反序列化它会爆炸。但是,如果您将其更改为使用支持字段,它就不会爆炸。

请参阅Introducing XML Serialization 并注意第 3 段:

XML 序列化不会转换方法、索引器、私有字段、 或只读属性(只读集合除外)。序列化 对象的所有字段和属性,无论是公共的还是私有的,都使用 BinaryFormatter 而不是 XML 序列化。

【讨论】:

  • 谢谢,我错过了。那么,在我的情况下,最佳实践是什么?我总是希望邻居属性是邻居的名称,所以我可以只使用private Grandparent _neighbor; private string _neighborName; 并设置设置器:public string NeighborName { get { return _neighbor != null ? _neighbor.Name : _neighborName; } set { _neighborName = value; } }。我认为这是一个糟糕的解决方案,因为使用此类的人可能认为他可以设置 NeighborName 属性,但它只是被真实邻居属性的名称所掩盖(我不想更改该对象的道具)。
  • 如果总是反序列化创建,可以设置一个私有标志,只设置一次,之后抛出无效操作异常
猜你喜欢
  • 1970-01-01
  • 2021-03-28
  • 2011-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多