tags:

views:

586

answers:

2

I've got an XML file which I use to create objects, change the objects, then save the objects back into the XML file.

What do I have to change in the following code so that it extracts a node from the XML based on the id, replaces that node with the new one, and saves it back into the XML?

The following gives me 'System.Xml.Linq.XElement' does not contain a constructor that takes '0' arguments':

//GET ALL SMARTFORMS AS XML
XDocument xmlDoc = null;
try
{
    xmlDoc = XDocument.Load(FullXmlDataStorePathAndFileName);
}
catch (Exception ex)
{
    HandleXmlFileNotFound(ex);
}

//EXTRACT THE NODE THAT NEEDS TO BE REPLACED
XElement oldElementToOverwrite = xmlDoc.Descendants("smartForm")
    .Where(sf => (int)sf.Element("id") == 2)
    .Select(sf => new XElement());

//CREATE THE NODE THAT WILL REPLACE IT
XElement newElementToSave = new XElement("smartForm",
                              new XElement("id", this.Id),
                              new XElement("idCode", this.IdCode),
                              new XElement("title", this.Title)
                              );

//OVERWRITE OLD WITH NEW
oldElementToOverwrite.ReplaceWith(newElementToSave);

//SAVE XML BACK TO FILE
xmlDoc.Save(FullXmlDataStorePathAndFileName);

XML file:

<?xml version="1.0" encoding="utf-8" ?>
<root>
  <smartForm>
    <id>1</id>
    <whenCreated>2008-12-31</whenCreated>
    <itemOwner>system</itemOwner>
    <publishStatus>published</publishStatus>
    <correctionOfId>0</correctionOfId>
    <idCode>customerSpecial</idCode>
    <title>Edit Customer Special</title>
    <description>This form has a special setup.</description>
    <labelWidth>200</labelWidth>
  </smartForm>
  <smartForm>
    <id>2</id>
    <whenCreated>2008-12-31</whenCreated>
    <itemOwner>system</itemOwner>
    <publishStatus>published</publishStatus>
    <correctionOfId>0</correctionOfId>
    <idCode>customersMain</idCode>
    <title>Edit Customer</title>
    <description>This form allows you to edit a customer.</description>
    <labelWidth>100</labelWidth>
  </smartForm>
  <smartForm>
    <id>3</id>
    <whenCreated>2008-12-31</whenCreated>
    <itemOwner>system</itemOwner>
    <publishStatus>published</publishStatus>
    <correctionOfId>0</correctionOfId>
    <idCode>customersNameOnly</idCode>
    <title>Edit Customer Name</title>
    <description>This form allows you to edit a customer's name only.</description>
    <labelWidth>100</labelWidth>
  </smartForm>
</root>
+2  A: 

Well, the error has nothing to do with saving, or even with replacement - it has to do with you trying to create an XElement without specifying the name. Why are you trying to use Select at all? My guess is you just want to use Single:

XElement oldElementToOverwrite = xmlDoc.Descendants("smartForm")
    .Where(sf => (int)sf.Element("id") == 2)
    .Single();

(As Noldorin notes, you can give Single a predicate to avoid using Where at all. Personally I quite like to split the two operations up, but they'll be semantically equivalent.)

That will return the single element in the sequence, or throw an exception if there are 0 elements or more than one. Alternatives are to use SingleOrDefault, First, or FirstOrDefault:

  • SingleOrDefault if it's legal to have 0 or 1
  • First if it's legal to have 1 or more
  • FirstOrDefault if it's legal to have 0 or more

If you're using an "OrDefault" one, the result will be null if there are no matches.

Jon Skeet
Single has an overload that takes a predicate, so no need to use `Where`.
Noldorin
Yup - I had forgotten about that, but I actually find it more readable to use the separate operations anyway :)
Jon Skeet
+2  A: 

I think the problem is simply your use of the Select call in the statement assigning oldElementToOverwrite. You actually seem to want the Single extension method.

XElement oldElementToOverwrite = xmlDoc.Descendants("smartForm")
    .Single(sf => (int)sf.Element("id") == 2)
Noldorin