Search code examples
.netxmlxsltbase-class-library

Difference between XElement.CreateNavigator() and XPathDocument().CreateNavigator()


The following test fails. r1 seems to be missing angle brackets, does anyone know way? I imagine its some sort of encoding error?

var nav1 = XElement.Load(stream).CreateNavigator(); 
var nav2 = new XPathDocument(stream).CreateNavigator();

using (var r1 = new StringWriter())
using (var r2 = new StringWriter())
{
  xslt.Transform(nav1, null, r1);
  xslt.Transform(nav2, null, r2);

  r1.ToString().Should().Equal(r2.ToString());
}

The problem here is not as far as I can tell semantically equivalent but lexically different xml, but that the resulting xml in the r1 case is missing it's xml tags. Interestingly, using var nav3 = XElement.Load(stream).CreateReader(); works fine.


Solution

  • Comparing two XML documents using their string representation is not a good idea. While the two documents may be equivalent, they still could have many lexical differences, among them:

    • Different prefixes bound to the same namespace.
    • Use/not use CDATA sections
    • represent/not-represent as strings inherited namespace nodes
    • use quotes vs apostrophes
    • use vs not-use character entities.
    • ..., etc.

    I would recommend a better way of doing this -- there are many xmldiff tools. You could also build your own XML comparison tool (as I have done for myself) based on my answer to this question.

    UPDATE:

    After clarifications from the OP:

    "The problem here is not as far as I can tell semantically equivalent but lexically different xml, but that the resulting xml in the r1 case is missing it's xml tags"

    I investigated and came up with the following conclusion:

    The XslCompiledTransform.Transform(IXPathNavigable, XsltArgumentList, Stream) works with either XmlDocument or XPathDocument as the first argument, as is described in the MSDN documentation.

    Parameters     
       inputType: System.Xml.XPath.IXPathNavigable An object    
                  implementing the IXPathNavigable interface. In the Microsoft .NET Framework, this
                  can  be either an XmlNode (typically an XmlDocument), or 
                  an XPathDocument containing the data to be transformed.
    

    As the Navigator created from an XElement obviously doesn't fit in this category, the reported problem is observed.

    Solution:

    Change:

    var nav1 = XElement.Load(stream).CreateNavigator();
    

    to:

    var nav1 = XDocument.Load(stream).CreateNavigator();
    
          
    

    Here is complete code that has been verified to work as expected:

    using System.IO;
    using System.Xml.Linq;
    using System.Xml.XPath;
    using System.Xml.Xsl;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    
    namespace TestLINQ_Xml
    {
        class Program
        {
            static void Main(string[] args)
            {
                test();
            }
    
            static void test()
            {
                XslCompiledTransform xslt = new XslCompiledTransform();
                xslt.Load(@"C:\temp\delete\XSLT.xsl");
    
                FileStream stream = 
                  new FileStream(@"C:\temp\delete\sourceXML.xml", FileMode.Open);
    
                var xdoc = XDocument.Load(stream);
                var nav1 = xdoc.CreateNavigator();
    
                stream.Seek(0L, SeekOrigin.Begin);
                var nav2 = new XPathDocument(stream).CreateNavigator(); 
    
                using (var r1 = new StringWriter()) using (var r2 = new StringWriter()) 
                {   xslt.Transform(nav2, null, r1); 
                    xslt.Transform(nav1, null, r2); 
    
                    string  res1 = r1.ToString();
                    string  res2 = r2.ToString();
    
                    Assert.AreEqual(res1, res2);
                } 
            }
        }
    }