使用父对象引用进行 XML 反序列化

2024-05-12

我有一个描述网站的 XML 文件。它由站点作为根节点组成,可以有页面,页面可以有按钮或文本框和对话框等对象。对话框也可以有对象。

在相应的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>

因为你没有任何机制来保证Parent and Children属性保持同步,最简单的方法就是告诉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 Collection<Element> Children { get; set; } with public ReadOnlyCollection<Element> Children { get; }。然后将孩子保留在私人列表中,然后返回list.AsReadOnly()。完成此操作后,您现在可以保证子列表在Parent setter:

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;
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用父对象引用进行 XML 反序列化 的相关文章

随机推荐