0

I am attempting to create a tool from WPF that creates a simple XML document by inputting data into a text box, then clicking a button to enter that data into the XML document. Other than the root element, I will have a step element with a child sub step element such as this:

<root>
<step id="1">
    <P>some instructions</p>
    <step id="1.1">
        <p>some additional instructions</p>
    </step>
</step>
<step id="2">
    <p>second set of instructions</p>
    <step id="2.1">
        <p>additional instructions for step 2</p>
    </step>
</step>

I have been able to add the parent steps, however all of my sub steps fall under the first parent step:

<root>
<step id="1">
  <step id="1.1">
  <step id="2.1">
  <step id="3.1">
<step id="2">
<step id="3">

I am trying to use XPath to insert my sub steps into the proper parent-steps. However I am receiving the error: "XAttribute does not contain a definition for 'add' and no extension method 'add' accepting a first argument type 'XAttribute' could be found."

I have searched extensively and am unable to come up with a solution, hopefully this makes sense and someone could give me a hand. Thank you in advance. Here is my code:

using System.Windows;
using System.Xml.Linq;
using System.Xml.XPath;

namespace XMLwpfTest1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        XDocument doc = new XDocument(
           new XElement("Content",
           new XElement("Title", "DM Title"))
          );

        string par = "par-";

        int counter = 1;
        int counter2 = 1;
        string stepNumber = "";



        public MainWindow()
        {
            InitializeComponent();
        }

        private void create_Click(object sender, RoutedEventArgs e)
        {
            doc.Element("Content").Add(
                new XElement("proceduralStep",
                new XAttribute("id", par + counter.ToString("D4")),
                new XElement("para",
            dataBox.Text)));
            stepNumber = par + counter.ToString("D4");
            counter += 1;
            dataBox.Clear();
            counter2 = 1;
        }


        private void createSubStep_Click(object sender, RoutedEventArgs e)
        {

            var addStep = doc.XPathSelectElement("Content/proceduralStep").Attribute(stepNumber);


            addStep.add(
            new XElement("proceduralStep",
            new XAttribute("id", stepNumber + "-" + counter2.ToString("D4")),
            new XElement("para",
        subDataBox.Text)));
            counter2 += 1;
            subDataBox.Clear();
        }

        private void save_Click(object sender, RoutedEventArgs e)
        {
            doc.Save(fileName.Text + ".xml");
            savedLabel.Visibility = Visibility.Visible;
        }
    }
}
swm462
  • 3
  • 3
  • Is this the full xml file? Where do Content/proceduralStep and content come from? I don't see them in the xml – Sybren May 11 '16 at 17:38
  • The XML I put in the question is only the structure I was looking for. Those aren't the actual tag names. i should have put the actual tag names I am using. Sorry for the confusion. The and are the elements that will be created. – swm462 May 11 '16 at 18:22

1 Answers1

0

Your problem comes at this line:

var addStep = doc.XPathSelectElement("Content/proceduralStep").Attribute(stepNumber);

It looks like you want to select the <proceduralStep> whose id attribute has value stepNumber. To do this with XPath, you need to use the syntax [@attributeName='attributeValue'], i.e.:

var addStep = doc.XPathSelectElement(string.Format("Content/proceduralStep[@id='{0}']", stepNumber));

Note that, if stepNumber were a user-entered string, you would need to be careful to prevent XPath injection, for instance by following the instructions here.

Alternatively, you could do this using by appending a Where expression:

var addStep = doc.XPathSelectElements("Content/proceduralStep")
    .Where(e => (string)e.Attribute("id") == stepNumber)
    .FirstOrDefault();

XPath injection is not an issue with this approach.

(The method you are currently calling, XElement.Attribute(XName), returns the attribute with the specified name, which you do not want.)

Then add() needs to be correctly capitalized as XElement.Add():

addStep.Add(
new XElement("proceduralStep",
new XAttribute("id", stepNumber + "-" + counter2.ToString("D4")),
new XElement("para", subDataBox.Text)));
dbc
  • 104,963
  • 20
  • 228
  • 340