I am trying to produce the following XML using the class structure below.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<VirtualDesktopManager>
<Categories>
<Category Name="Category 1">
<Desktops>
<Desktop Name="Desktop 1">
<Applications Name="Application 1" />
<Applications Name="Application 2" />
</Desktop>
</Desktops>
</Category>
</Categories>
</VirtualDesktopManager>
When executing the code below, I get the exception: System.ArgumentException: 'Cannot insert a node or any ancestor of that node as a child of itself.'
. The classes themselves do not have any circular references so I must be doing something wrong.
private static void Main ()
{
var database = new Database();
var category = new VirtualDesktopCategory();
var desktop = new VirtualDesktop();
var application = new VirtualDesktopApplication();
category = new VirtualDesktopCategory() { Name = "Cat 1", };
database.Categories.Add(category);
desktop = new VirtualDesktop() { Name = "Desktop 1", };
category.Desktops.Add(desktop);
application = new VirtualDesktopApplication() { Name = "Application 1", };
desktop.Applications.Add(application);
application = new VirtualDesktopApplication() { Name = "Application 2", };
desktop.Applications.Add(application);
database.ToXmlDocument().InnerText.Dump();
}
public class Database
{
public string Name { get; set; } = "";
public List<VirtualDesktopCategory> Categories { get; private set; } = new();
public XmlDocument ToXmlDocument()
{
var document = new XmlDocument();
var xml = $@"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes"" ?><VirtualDesktopManager></VirtualDesktopManager>";
document.LoadXml(xml);
document.DocumentElement?.AppendChild(this.ToXmlElement(document, document.DocumentElement));
return document;
}
public XmlElement ToXmlElement(XmlDocument document, XmlElement elementParent)
{
var elementCategories = document.CreateElement("Categories");
elementParent.AppendChild(elementCategories);
foreach (var category in this.Categories)
{
// System.ArgumentException: Cannot insert a node or any ancestor of that node as a child of itself.
elementCategories.AppendChild(category.ToXmlElement(document, elementCategories));
}
return elementCategories;
}
}
public class VirtualDesktopCategory
{
public string Name { get; set; } = "";
public List<VirtualDesktop> Desktops { get; private set; } = new();
public XmlElement ToXmlElement(XmlDocument document, XmlElement elementParent)
{
var elementCategory = document.CreateElement("Category");
var elementDesktops = document.CreateElement("Desktops");
elementCategory.AppendAttribute(document, nameof(this.Name), this.Name);
elementParent.AppendChild(elementCategory);
elementCategory.AppendChild(elementDesktops);
foreach (var desktop in this.Desktops)
{
elementDesktops.AppendChild(desktop.ToXmlElement(document, elementDesktops));
}
return elementCategory;
}
}
public class VirtualDesktop
{
public string Name { get; set; } = "";
public List<VirtualDesktopApplication> Applications { get; private set; } = new();
public XmlElement ToXmlElement(XmlDocument document, XmlElement elementParent)
{
var elementDesktop = document.CreateElement("Desktop");
var elementApplications = document.CreateElement("Applications");
elementDesktop.AppendAttribute(document, nameof(this.Name), this.Name);
elementParent.AppendChild(elementDesktop);
elementDesktop.AppendChild(elementApplications);
foreach (var application in this.Applications)
{
elementApplications.AppendChild(application.ToXmlElement(document, elementApplications));
}
return elementParent;
}
}
public class VirtualDesktopApplication
{
public string Name { get; set; } = "";
public XmlElement ToXmlElement(XmlDocument document, XmlElement elementParent)
{
var elementApplication = document.CreateElement("Application");
elementApplication.AppendAttribute(document, nameof(this.Name), this.Name);
elementParent.AppendChild(elementApplication);
return elementApplication;
}
}
public static class Extensions
{
public static XmlAttribute AppendAttribute(this XmlElement element, XmlDocument document, string name, string value)
{
var attribute = document.CreateAttribute(name);
attribute.Value = value;
element.Attributes.Append(attribute);
return attribute;
}
}
Any pointers would be appreciated.
public class VirtualDesktop
{
public string Name { get; set; } = "";
public List<VirtualDesktopApplication> Applications { get; private set; } = new();
public XmlElement ToXmlElement(XmlDocument document, XmlElement elementParent)
{
var elementDesktop = document.CreateElement("Desktop");
var elementApplications = document.CreateElement("Applications");
elementDesktop.AppendAttribute(document, nameof(this.Name), this.Name);
elementParent.AppendChild(elementDesktop);
elementDesktop.AppendChild(elementApplications);
foreach (var application in this.Applications)
{
elementApplications.AppendChild(application.ToXmlElement(document, elementApplications));
}
return elementParent;
}
}
You're returning elementParent
, rather than elementDesktop
.
You had mis-identified the line throwing the exception. It was actually:
elementDesktops.AppendChild(desktop.ToXmlElement(document, elementDesktops));
So, something in desktop.ToXmlElement
was the cause. A quick bit of trial-and-error shows that commenting out everything in VirtualDesktop.ToXmlElement
doesn't fix it, so it's somewhere between the var elementDesktop = document.CreateElement("Desktop")
and return elementParent;
... Wait...
You're also serializing the XmlDocument incorrectly. You need to do something like:
using var sr = new StringWriter();
using (var writer = XmlWriter.Create(sr))
{
database.ToXmlDocument().Save(writer);
}
Console.WriteLine(sr.ToString());
However, this will give you:
<?xml version="1.0" encoding="utf-16" standalone="yes"?>
To fix the encoding, you'll need to do something like this: https://stackoverflow.com/a/1564727/1086121
Working example, with the duplicate lines identified by @EyesShriveledToRaisins commented out.