Click here to Skip to main content
15,893,588 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
I am trying to create an XML document as output using C# with the parent node being based on the count of tabs from the input file. I am trying to use XPath to determine the parent node. I have read the source file in and saved it as a list, and I am able to get the parts of the data saved as the attributes that I need, but the child nodes are not in the expected order. It is putting them in the expected "level", but not under the correct parent. My XPath seems to not be getting the parent that I expect it to, but I am not sure what is wrong with the XPath. It seems to work down to Filename8 (in the example), but it would put Filename9 under Filename 7 and then Filename8 would be found lower down in the tree. Of course this is a small sample of the input file, but I hope you will understand what I am trying to do. Any suggestions would be greatly appreciated!
Here is part of my code:
C#
//Loop through the list to create the child nodes
            for (int i = 0; i < lines.Count; i++)
            {
              // if the line starts with /* then it is a comment, 
              // skip and go to next line
                if (lines[i].StartsWith("/*"))
                {
                    continue;
                }
                tabCount = 0;
                tabCount = lines[i].Count(c => c == '\t');  

                //get the title of the line
                if (lines[i].Contains("/*"))
                {
                    titleIndex = lines[i].IndexOf("/*");
                    endTitleIndex = lines[i].IndexOf("*/");
                    title = lines[i].Substring(titleIndex + 2, endTitleIndex - titleIndex - 2).Trim();
                }
                else
                {
                    titleIndex = lines[i].Length;
                }

                //get the file name
                filename = lines[i].Substring(0, titleIndex).Trim();

                //create the element and add the filename attribute
                XmlElement tocEntry = doc.CreateElement("tocEntry");   
                tocEntry.SetAttribute("filename", filename);
                
                //if the title not blank add title attribute
                if (title.Length > 0)
                {
                    tocEntry.SetAttribute("title", title);
                }

                //Add in a tabCount attribute to help determine the parent node.
                tocEntry.SetAttribute("tabCount", tabCount.ToString());

                //If the tabCount is 0 then add the node to the root
                if (tabCount == 0)
                {
                    root.AppendChild(tocEntry);
                }
                //If the tabCount is not 0 then add as a child to the parent.
                else 
                {
                    XmlNode parent = root.SelectSingleNode("//tocEntry[@tabCount='" + (tabCount - 1) + "'][last()]");
                    parent.AppendChild(tocEntry);
                }               
               
            }

Here is a small sample of the input file:
..\filename1.xml /* TITLE 1 */
/t..\filename2.xml /* TITLE 2 */
/t/t..\filename3.xml /* TITLE 3 */
/t/t..\filename4.xml /* TITLE 4 */
/t/t..\filename5.xml /* TITLE 5 */
/t/t..\filename6.xml /* TITLE 6 */
/t/t..\filename7.xml /* TITLE 7 */
/t..\filename8.xml /* TITLE 8 */
/t/t..\filename9.xml /* TITLE 9 */
/t/t/t..\filename10.xml /* TITLE 10 */
/t/t/t/t..\filename11.xml /* TITLE 11 */

Desired output sample:
XML
<Entry filename="..\filename1.xml" title="TITLE 1">
     <Entry filename="..\filename2.xml" title="TITLE 2">
        <Entry filename="..\filename3.xml" title="TITLE 3"/>
        <Entry filename="..\filename4.xml" title="TITLE 4"/>
        <Entry filename="..\filename5.xml" title="TITLE 5"/>
        <Entry filename="..\filename6.xml" title="TITLE 6"/>
        <Entry filename="..\filename7.xml" title="TITLE 7"/>
     </Entry>
     <Entry filename="..\filename8.xml" title="TITLE 8">
        <Entry filename="..\filename9.xml" title="TITLE 9">
           <Entry filename="..\filename10.xml" title="TITLE 10">
               <Entry filename="..\filename11.xml" title="TITLE 11"/>
           </Entry>
        </Entry>
     </Entry>
 </Entry>
Posted
Comments
Erik Rude 20-Mar-12 12:40pm    
Do you have to use XPath? Could you get away by just writing the XML file in a text file using streamwriter?
VonBryant 20-Mar-12 14:01pm    
I suppose that I might could use streamwriter, but the source file is 1000's of lines, and I think it might get confusing trying to figure out when to close the child tags and which one is a sibling of which, parent of which, etc. This is a very small sample of the input and output. To avoid confusion and keep the relationships straight I was trying to use XPath.
pietvredeveld 20-Mar-12 14:27pm    
Are you sure the root variable points to the document root. It looks like it is pointing to the node holding the filename1.xml entry

Piet
VonBryant 20-Mar-12 15:45pm    
Well, the root element is named "toc" so the code for that (which I didn't include) looks like this:
XmlElement root = doc.CreateElement("toc");
doc.AppendChild(root);
VonBryant 20-Mar-12 15:46pm    
I just realized that in my output example it should be tocEntry not Entry (from what my code has). That is what I get for trying to hurry!

1 solution

Don't see the error in you're xpath, but when parsing the line with filename10.xml the query returns to nodes (filename7.xml and filename9.xml). SelectSingleNode selects the first node (filename7.xml).

So when you change
XmlNode parent = root.SelectSingleNode("//tocEntry[@tabCount='" + (tabCount - 1) + "'][last()]");
parent.AppendChild(tocEntry);


it into


XmlNodeList parent = root.SelectNodes("//tocEntry[@tabCount='" + (tabCount - 1) + "'][last()]");
parent[parent.Count -1].AppendChild(tocEntry);


you get the expected result

Regards

Piet
 
Share this answer
 
Comments
VonBryant 21-Mar-12 9:58am    
Thank you so much for your help!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900