我有一个描述网站的XML文件。它由Site作为根节点组成,可以有Pages,Pages可以有对象,如Button或TextBox和Dialogs。对话框也可以有对象。
在相应的C#类中,所有类都派生自Element。当我反序列化XML时,如何引用构造元素的Parent?
我被告知复杂类型不能以这种方式反序列化,但如果我在XML中为每个元素添加一个ID字段,那么可以使用它来引用父类,因此可以正确地反序列化。我该如何实现?我不想手动将ID字段添加到XML文件中的每个元素......
我的元素类:
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>
答案 0 :(得分:0)
由于您没有任何机制来保证Parent
和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; }
替换为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;
}
}
}