【问题标题】:XML deserialization with parent object reference带有父对象引用的 XML 反序列化
【发布时间】:2015-07-15 07:49:37
【问题描述】:

我有一个描述网站的 XML 文件。它由 Site 作为根节点组成,可以有 Pages,Pages 可以有 Button 或 TextBox 和 Dialogs 等对象。对话框也可以有对象。

在对应的C#类中,都是从Element派生的。当我反序列化 XML 时,我如何才能获得对正在构造的元素的父级的引用?

我被告知复杂类型不能以这种方式反序列化,但如果我向 XML 中的每个元素添加一个 ID 字段,则可以使用它来引用父级,从而正确反序列化。我将如何实施?我不想手动为 XML 文件中的每个元素添加一个 ID 字段...

我的元素类:

public class Element 
{
    public string Name { get; set; }
    public string TagName { get; set; }
    public string XPath { get; set; }

    [XmlElement(ElementName = "Site", Type = typeof(Site))]
    [XmlElement(ElementName = "Page", Type = typeof(Page))]
    [XmlElement(ElementName = "Dialog", Type = typeof(Dialog))]
    public Element Parent { get; set; }

    [XmlArray("Children", IsNullable = false)]
    [XmlArrayItem(Type = typeof(TextBox))]
    [XmlArrayItem(Type = typeof(Page))]
    [XmlArrayItem(Type = typeof(Button))]
    [XmlArrayItem(Type = typeof(Dialog))]
    public Collection<Element> Children { get; set; }

}

我的反序列化:

public Site GetSiteFromXml(string filePath, string fileName)
{
    XmlSerializer serializer = new XmlSerializer(typeof(Site));
    return serializer.Deserialize(new XmlTextReader(Path.Combine(filePath, fileName))) as Site;
}

我的 XML 文件:

<Site>
  <Name>WebSiteName</Name>
  <Url>https://site.url</Url>
  <Children>
    <Page>
      <Name>HomePage</Name>
      <Address>#page=1</Address>
      <Children>
        <Button>
          <Name>LoginDialogButton</Name>
          <Id>LoginIcon</Id>
          <XPath>//*[@id="LoginIcon"]</XPath>
          <Enabled>true</Enabled>
          <Action>OpenLoginDialog</Action>
        </Button>
        <Dialog>
          <Name>LoginPopUpDialog</Name>
          <Id>loginModal</Id>
          <Children>
            <TextBox>
              <Name>UserNameInput</Name>
            </TextBox>
            <TextBox>
              <Name>PasswordInput</Name>
            </TextBox>
            <Button>
              <Name>LoginButton</Name>
              <Action>DialogDismiss</Action>
            </Button>
          </Children>
        </Dialog>
      </Children>
    </Page>
  </Children>
</Site>

【问题讨论】:

  • 有点相关,但在这种情况下,父对象和子对象是不同的类。在我的情况下,它们是相同的。我昨天看到了这篇文章,并试图让它对我有用,但失败了。

标签: c# xml


【解决方案1】:

由于您没有任何机制来保证ParentChildren 属性保持同步,因此最简单的方法是告诉XmlSerializer 忽略这两个属性,并添加一个代理将子列表作为数组(不是集合)返回的属性。在代理属性的 setter 中,填充 Children 集合并设置每个子项的 Parent 属性:

public class Element
{
    public Element() { Children = new Collection<Element>(); }

    public string Name { get; set; }
    public string TagName { get; set; }
    public string XPath { get; set; }

    [XmlIgnore]
    public Element Parent { get; set; }

    [XmlIgnore]
    public Collection<Element> Children { get; set; }

    [XmlArray("Children", IsNullable = false)]
    [XmlArrayItem(Type = typeof(TextBox))]
    [XmlArrayItem(Type = typeof(Page))]
    [XmlArrayItem(Type = typeof(Button))]
    [XmlArrayItem(Type = typeof(Dialog))]
    [XmlArrayItem(Type = typeof(Site))]
    public Element[] ChildArrayCopy
    {
        get
        {
            return Children.ToArray();
        }
        set
        {
            Children.Clear();
            if (value != null)
                foreach (var child in value)
                    child.SetParent(this);
        }
    }
}

public static class ElementExtensions
{
    public static void SetParent(this Element child, Element parent)
    {
        if (child == null)
            throw new ArgumentNullException();
        if (child.Parent == parent)
            return; // Nothing to do.
        if (child.Parent != null)
            child.Parent.Children.Remove(child);
        child.Parent = parent;
        if (parent != null)
            parent.Children.Add(child);
    }
}

您的 XML 现在可以成功反序列化和序列化了。

顺便说一句,我会考虑用public ReadOnlyCollection&lt;Element&gt; Children { get; } 替换您的public Collection&lt;Element&gt; Children { get; set; }。然后将孩子保留在私人列表中,并返回list.AsReadOnly()。这样做后,您现在可以保证子列表在 Parent 设置器中保持最新:

public class Element
{

    public Element() { }

    public string Name { get; set; }
    public string TagName { get; set; }
    public string XPath { get; set; }

    private readonly List<Element> children = new List<Element>();
    private Element parent = null;

    [XmlIgnore]
    public Element Parent
    {
        get
        {
            return parent;
        }
        set
        {
            if (parent == value)
                return;
            if (parent != null)
                parent.children.Remove(this);
            parent = value;
            if (parent != null)
                parent.children.Add(this);
        }
    }

    [XmlIgnore]
    public ReadOnlyCollection<Element> Children
    {
        get
        {
            return children.AsReadOnly();
        }
    }

    [XmlArray("Children", IsNullable = false)]
    [XmlArrayItem(Type = typeof(TextBox))]
    [XmlArrayItem(Type = typeof(Page))]
    [XmlArrayItem(Type = typeof(Button))]
    [XmlArrayItem(Type = typeof(Dialog))]
    [XmlArrayItem(Type = typeof(Site))]
    public Element[] ChildArrayCopy
    {
        get
        {
            return Children.ToArray();
        }
        set
        {
            if (value != null)
                foreach (var child in value)
                    child.Parent = this;
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多