映射和序列化双向关联

时间:2014-07-01 12:20:08

标签: c# xml serialization

我想,这将是一个微不足道的问题,并会立即找到答案,但不知何故互联网证明我的假设是错误的。

我有一个包含几个实体的模型,这些实体之间有所有可能的关联(1对1,1对多,多对多,聚合,组合等)。为简单起见,我们以此为例。我们有一个Person类和一个Car类。这个人可以拥有多辆汽车,但一辆汽车只能属于一个人(所以一对多关系)。现在,在Person中创建List / ArrayList来跟踪他/她的汽车是非常简单的。但是,我想跟踪车主的情况。所以,这些类看起来像这样:

[Serializable]
public class Person
{
    [XmlElement]
    public List<Car> Cars { get; set; }

    [XmlAttribute]
    public string Name { get; set; }

    public Person()
    {
        Cars = new List<Car>();
    }
}

[Serializable]
public class Car
{
    [XmlElement]
    public Person Owner { get; set; }

    [XmlAttribute]
    public string Type { get; set; }

    public Car()
    {
    }
}

但是,我相信,这种结构会在Xml文件中引起无限循环,如下所示:

<Person name="John Doe">
  <Cars>
    <Car type="Ford">
      <Owner>
        <Person name="John Doe">
          <Cars>
            <Car type="Ford">
              <Owner>
                <Person name="John Doe">
      ... etc.

我甚至尝试了它,在序列化过程中,我得到了“你需要将XmlChoiceIdentifierAttribute添加到'Owner'成员。”例外。

所以,我有几个问题:  1.有没有办法阻止循环序列化?  2.如果没有,我是否必须为每个类编写自己的序列化器?  这个映射可以吗?还是有其他/更好的方法?我已经考虑过一个中心的'Mapper'类,它会根据ID返回所需的对象......但是,这可以通过SQL完成。我想避免SQL(因为保持应用程序轻量化)。

1 个答案:

答案 0 :(得分:3)

XmlSerializer是一个树序列化器,而不是图形序列化器。最好的办法是在序列化期间避免向后导航,例如:

[XmlIgnore]
public Person Owner { get; set; }

(&#34;父母&#34;和&#34;所有者&#34;几乎总是向后导航)

不幸的是,XmlSerializer不支持后序列化回调,否则可以添加如下内容:

[WhateverOnAfterDeserialized]
public void OnAfterDeserialized(...) {
    foreach(var car in cars) car.Owner = this;
}

其他一些序列化程序支持序列化回调,但是通过该令牌,其他序列化程序也可能支持全图序列化。例如,DataContractSerializer可以同时支持回调和完整图形,但它对xml的控制要少得多。

另一种选择是拥有一个自定义集合类型,在添加/删除时维护父属性;例如:

public class Person
{
    private readonly CarCollection cars;
    public Person() {
        cars = new CarCollection(this);
    }
    [XmlElement]
    public CarCollection Cars { get { return cars; } } 

    ...
}

with(CarCollection):

// add code...
innerList.Add(value);
value.Parent = parent; // this is the field stored in the constructor

就个人而言,我认为这可能有点过头了。

另一种选择是简单地添加一个在反序列化后调用的修复方法,它可以执行任何必要的操作(并向下级联):

public void FixupAfterDeserializer() {
    foreach(var car in cars) car.Parent = this;
}

请注意,您必须手动调用此方法。

最后,请注意[Serializable]不需要XmlSerializer