Search code examples
c#xmllinq-to-xmlxelement

C# - Unable to save a customized linq-to-xml file, with new elements in it?


I'm creating a new XDocument and inserting a root element "profiles" in it, then saving.

if (!System.IO.File.Exists("profiles.xml"))
{
    XDocument doc = new XDocument(
        new XElement("profiles")
    );
    doc.Save("profiles.xml", SaveOptions.None);
}

And then later I wanna take users input and add profiles into the already created xml file:

XElement profile =
        new XElement(Player.Name,
            new XElement("level", Player.Level),
            new XElement("cash", Player.Cash)
        );

XDocument doc = XDocument.Load("profiles.xml");
List<XElement> profiles = doc.Root.Elements().ToList();

for (int i = 0; i < profiles.Count; i++)
{
    if (profiles[i].Name.ToString() == Player.name)
    {
        profiles[i] = profile;
        return;
    }
}
profile.Add(profile);
doc.Save("profiles.xml", SaveOptions.None);

But for some reason, it will never add any new profiles?

EDIT: Also, if I manually create a new profile into the xml file, it won't customize either, so the problem is within Saving the file?


Solution

  • You're never actually doing anything to change any of the elements within the XDocument that doc refers to:

    • If you find an element with the existing name, you're modifying the list, but that won't modify the document. You probably want to use XElement.ReplaceWith:

      profiles[i].ReplaceWith(profile);
      

      Note that in this case you're not even trying to save the XML file again (due to the return statement), so it's not really clear what you're trying to achieve in this case.

    • If you don't find the element, you're adding the profile element to itself, which certainly isn't going to modify the document. I suspect you want:

      doc.Root.Add(profile);
      

      In other words, add the new profile element as a new final child of the root element.

    EDIT: Here's a different approach to try instead - I'm assuming any one name should only occur once:

    XDocument doc = XDocument.Load("profiles.xml");
    var existingElement = doc.Root
                             .Elements()
                             .Where(x => x.Name.ToString() == Player.name)
                             .FirstOrDefault();
    if (existingElement != null)
    {
        existingElement.ReplaceWith(profile);
    }
    else
    {
        doc.Root.Add(profile);
    }
    doc.Save("profiles.xml", SaveOptions.None);
    

    Also, I would strongly advise you not to use the player name as the element name. Use it as an attribute value or text value instead, e.g.

    XElement profile =
            new XElement("player",
                new XAttribute("name", Player.Name),
                new Attribute("level", Player.Level),
                new XAttribute("cash", Player.Cash)
            );
    

    That way you won't have problems if the player name has spaces etc. You'd then need to change your query to:

    var existingElement = doc.Root
                             .Elements()
                             .Where(x => (string) x.Attribute("name)" == Player.name)
                             .FirstOrDefault();