XPathNavigator Null Xml Bug?

If this is a way to represant a null xml entries: <name/>, then XPathNavigator doesn't like that cause it can't determine the value of a node properly with those scattered through the document...Here's code that should show you what i mean... the output is this:

Expected: 0 1 2 3 4 5
Actual : 01 2 3 45
Press any key to continue . . .

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.XPath;
using System.IO;
using System.Collections;

namespace XpathNavigatorBugConsoleApplication
{
class Program
{
public static XPathDocument StringToXml(string xml)
{
if (String.IsNullOrEmpty(xml))
throw new ArgumentNullException("xml");

StringReader stringReader = new StringReader(xml);

XPathDocument xPathDocument = new XPathDocument(stringReader);

stringReader.Close();

return xPathDocument;
}

static void Main(string[] args)
{
FindValuesTest();
}

private static void FindValuesTest()
{
XPathDocument document = StringToXml(xml);

XPathNavigator navigator = document.CreateNavigator();

string expected = "0 1 2 3 4 5 ";
string actual = "";

ArrayList matches = FindValues(navigator, "one");

foreach (string i in matches)
actual += i + " ";

Console.WriteLine("Expected: " + expected);
Console.WriteLine("Actual : " + actual);
}

private static string xml =

#region Xml File
//< xml version="1.0" encoding="utf-8" >
//<one>
// <one>
// <one>0</one>
// <one/>
// <one>1</one>
// </one>
// <one>2</one>
// <one/>
// <one>3</one>
// <one/>
// <one>
// <one>
// <one>4</one>
// <one/>
// <one>5</one>
// <one/>
// </one>
// </one>
// <one/>
//</one>

"< xml version=\"1.0\" encoding=\"utf-8\" >" + Environment.NewLine +
"<one>" + Environment.NewLine +
" <one>" + Environment.NewLine +
" <one>0</one>" + Environment.NewLine +
" <one/>" + Environment.NewLine +
" <one>1</one>" + Environment.NewLine +
" </one>" + Environment.NewLine +
" <one>2</one>" + Environment.NewLine +
" <one/>" + Environment.NewLine +
" <one>3</one>" + Environment.NewLine +
" <one/>" + Environment.NewLine +
" <one>" + Environment.NewLine +
" <one>" + Environment.NewLine +
" <one>4</one>" + Environment.NewLine +
" <one/>" + Environment.NewLine +
" <one>5</one>" + Environment.NewLine +
" <one/>" + Environment.NewLine +
" </one>" + Environment.NewLine +
" </one>" + Environment.NewLine +
" <one/>" + Environment.NewLine +
"</one>";

#endregion

private static ArrayList FindValues(XPathNavigator navigator, string name)
{
if (navigator == null)
throw new ArgumentNullException("navigator");

if (String.IsNullOrEmpty(name))
throw new ArgumentNullException("name");

ArrayList matchedNameValues = new ArrayList();

navigator.MoveToRoot();

navigator.MoveToFirstChild();

do
{
//Find the first element.
if (navigator.NodeType == XPathNodeType.Element)
{
if (navigator.HasChildren)
{
navigator.MoveToFirstChild();

//Loop through all the children.
do
{
//Check and get the desired values
if (navigator.Name == name && !String.IsNullOrEmpty(navigator.Value)) //don't add blank values
matchedNameValues.Add(navigator.Value);

}
while (navigator.MoveToNext());
}
}
}
while (navigator.MoveToNext());

return matchedNameValues;
}
}
}



Answer this question

XPathNavigator Null Xml Bug?

  • Steve Roszko

    Checking names totally slipped from my mind...

    private static ArrayList FindValues(XPathNavigator navigator, string name)
    {
    if (navigator == null)
    throw new ArgumentNullException("navigator");

    if (String.IsNullOrEmpty(name))
    throw new ArgumentNullException("name");

    ArrayList matchedNameValues = new ArrayList();

    navigator.MoveToRoot();

    string parentsNameToPass = navigator.Name;
    navigator.MoveToFirstChild();

    matchedNameValues.AddRange(goElms(navigator, name, parentsNameToPass));

    return matchedNameValues;
    }
    private static ArrayList goElms(XPathNavigator navigator, string name, string parentsName)
    {

    ArrayList matches = new ArrayList();

    //Find the first element.
    if (navigator.NodeType == XPathNodeType.Element)
    {
    do
    {
    if (navigator.HasChildren)
    {
    string parentsNameToPass = navigator.Name;
    navigator.MoveToFirstChild();
    matches.AddRange(goElms(navigator, name, parentsNameToPass));
    }
    }
    while (navigator.MoveToNext());
    navigator.MoveToParent();
    }
    else if (navigator.NodeType == XPathNodeType.Text && parentsName.Equals(name))
    {
    matches.Add(navigator.Value);

    navigator.MoveToParent();
    }

    return matches;
    }
    }



  • Joe Szymanski

    Thx, worked great

  • Hugo Flores

    Well your code worked fine with the original test xml, but i've expanding my test to read this xml:

    < xml version="1.0" encoding="utf-8" >
    <one>
      <one>
        <one>0</one>
        <one/>
        <one>1</one>
        <trash>6</trash>
      </one>
      <one>2</one>
      <one/>
      <one>3</one>
      <one/>
      <one>
        <trash/>
        <trash>6</trash>
        <trash/>
        <one>
          <one>4</one>
          <one/>
          <trash/>
          <one>5</one>
          <one/>
        </one>
      </one>
      <one/>
    </one>

    And it just adds all the values. Obvioussly this is because the goElms doesn't do a element name check, however my attempts to add one seem to fail.

    private static ArrayList goElms(XPathNavigator navigator, string name)
            {         

                ArrayList matches = new ArrayList();

                    //Find the first element.
                    if (navigator.NodeType == XPathNodeType.Element)
                    {
                        do
                        {
                            if (navigator.HasChildren)
                            {
                                navigator.MoveToFirstChild();

                                matches.AddRange(goElms(navigator, name)));

                              
                            }
                        }
                        while (navigator.MoveToNext());
                        navigator.MoveToParent();
                    }
                    else if (navigator.NodeType == XPathNodeType.Text)
                    {
                        if(navigator.Name == name)
                            matches.Add(navigator.Value);

                        navigator.MoveToParent();
                    }

                return matches;
            }

    I keep getting Name = to "". Probably cause i'm putting it in the block with "else if (navigator.NodeType == XPathNodeType.Text)". Hmm


  • Michael Friedman

    Hi FocusedWolf,

    No, there is no bug, your do ... while loop is wrong. What you are doing is you get to first element (root) and then you move to it's child (document element <one>)  and then you move again to it's child (the second <one>) and you go through the elements that is at the same level of the second one (there are 7 of them) The value of

              <one>" + Environment.NewLine +
                <one>0</one>" + Environment.NewLine +
                <one/>" + Environment.NewLine +
                <one>1</one>" + Environment.NewLine +
              </one>" + Environment.NewLine +

    is "01", then comes "2", then "3", then "45".

    It's not working because you are not going deep enough.

    Try something like this: (this repleaces your FindValues method and introduces goElms method.

            private static ArrayList FindValues(XPathNavigator navigator, string name)
            {
                if (navigator == null)
                    throw new ArgumentNullException("navigator");

                if (String.IsNullOrEmpty(name))
                    throw new ArgumentNullException("name");

                ArrayList matchedNameValues = new ArrayList();

                navigator.MoveToRoot();

                navigator.MoveToFirstChild();

                matchedNameValues.AddRange(goElms(navigator));

                return matchedNameValues;
            }
            private static ArrayList goElms(XPathNavigator navigator)
            {
              

                ArrayList matches = new ArrayList();

                    //Find the first element.
                    if (navigator.NodeType == XPathNodeType.Element)
                    {
                        do
                        {
                            if (navigator.HasChildren)
                            {
                                navigator.MoveToFirstChild();

                                matches.AddRange(goElms(navigator));

                               
                            }
                        }
                        while (navigator.MoveToNext());
                        navigator.MoveToParent();
                    }
                    else if (navigator.NodeType == XPathNodeType.Text)
                    {
                        matches.Add(navigator.Value);

                        navigator.MoveToParent();
                    }

                return matches;
            }

     

    Thanks,

    Sinan



  • XPathNavigator Null Xml Bug?