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;
        }
    }
}
2
  • Is this the full xml file? Where do Content/proceduralStep and content come from? I don't see them in the xml Commented May 11, 2016 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 <Content> and <proceduralStep> are the elements that will be created. Commented May 11, 2016 at 18:22

1 Answer 1

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)));
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.