diff --git a/src/AngleSharp.XPath.Tests/AngleSharp.XPath.Tests.csproj b/src/AngleSharp.XPath.Tests/AngleSharp.XPath.Tests.csproj index 01e6733..5a2cd1b 100644 --- a/src/AngleSharp.XPath.Tests/AngleSharp.XPath.Tests.csproj +++ b/src/AngleSharp.XPath.Tests/AngleSharp.XPath.Tests.csproj @@ -1,15 +1,15 @@  - - - - + + + + - netcoreapp3.0 + net6.0 false - \ No newline at end of file + diff --git a/src/AngleSharp.XPath.Tests/HtmlDocumentNavigatorTests.cs b/src/AngleSharp.XPath.Tests/HtmlDocumentNavigatorTests.cs index 2c017ad..202e7fe 100644 --- a/src/AngleSharp.XPath.Tests/HtmlDocumentNavigatorTests.cs +++ b/src/AngleSharp.XPath.Tests/HtmlDocumentNavigatorTests.cs @@ -3,6 +3,8 @@ using AngleSharp.Html.Parser; using NUnit.Framework; using System.Threading.Tasks; +using System.Xml.XPath; +using AngleSharp.Dom; namespace AngleSharp.XPath.Tests { @@ -99,5 +101,77 @@ public void SelectSingleNode_DontIgnoreNamespaces_ShouldReturnNode() Assert.IsNotNull(node); Assert.That(node.NodeName, Is.EqualTo("xhtml:link")); } + + [Test] + public void SelectNodes_CanReturnAttribute() + { + // Arrange + var html = "
hello world
"; + var parser = new HtmlParser(); + var doc = parser.ParseDocument(html); + + // Act + var nodes = doc.DocumentElement.SelectNodes("//@*"); + + // Assert + Assert.IsNotNull(nodes); + Assert.That(nodes, Has.Count.EqualTo(2)); + Assert.That(nodes, Is.All.InstanceOf()); + } + + [Test] + public void TestNameXPathFunctionOnXMLDoc() + { + // Arrange + var xml = @"Test

Test

"; + var angleSharpXmlDoc = new XmlParser().ParseDocument(xml); + + // Act + var xmlNav = angleSharpXmlDoc.CreateNavigator(); + + // Assert + Assert.AreEqual(TagNames.Html, xmlNav.Evaluate("name()")); + } + + [Test] + public void TestNameXPathFunctionOnHTMLDoc() + { + // Arrange + var html = @"Test

Test

"; + + var angleSharpHtmlDoc = new HtmlParser().ParseDocument(html); + + // Act + var htmlNav = angleSharpHtmlDoc.CreateNavigator(); + + // Assert + Assert.AreEqual(TagNames.Html, htmlNav.Evaluate("name()")); + } + + [Test] + public void MoveToParent_CallWhenCurrentNodeIsAttr_ShouldBeMovedToAttrOwnerElement() + { + // Arrange + var xml = @"foo"; + var parser = new XmlParser(); + var doc = parser.ParseDocument(xml); + var nav = doc.CreateNavigator(false); + nav.MoveToChild("root", ""); + + // Act + + if (nav.MoveToFirstAttribute()) + { + do + { + Assert.AreEqual(nav.NodeType, XPathNodeType.Attribute); + } + while (nav.MoveToNextAttribute()); + nav.MoveToParent(); + } + + // Assert + Assert.AreEqual(nav.Name, "root"); + } } } diff --git a/src/AngleSharp.XPath/AngleSharp.XPath.csproj b/src/AngleSharp.XPath/AngleSharp.XPath.csproj index e802bd8..f116ac4 100644 --- a/src/AngleSharp.XPath/AngleSharp.XPath.csproj +++ b/src/AngleSharp.XPath/AngleSharp.XPath.csproj @@ -1,13 +1,13 @@  - 1.1.7 + 2.0.0 + 2.0.0 + 2.0.0 Denis Ivanov AngleSharp.XPath - 1.1.7 AngleSharp.XPath AngleSharp.XPath - netstandard2.0 - 1.1.7 + netstandard2.0;net461;net472;net6.0 XPath support for AngleSharp https://github.com/AngleSharp/AngleSharp.XPath/ MIT @@ -16,10 +16,15 @@ Key.snk true True + https://raw.githubusercontent.com/AngleSharp/AngleSharp.XPath/master/logo.png - + + + + + diff --git a/src/AngleSharp.XPath/HtmlDocumentNavigator.cs b/src/AngleSharp.XPath/HtmlDocumentNavigator.cs index 3638473..34d1051 100644 --- a/src/AngleSharp.XPath/HtmlDocumentNavigator.cs +++ b/src/AngleSharp.XPath/HtmlDocumentNavigator.cs @@ -10,7 +10,6 @@ public class HtmlDocumentNavigator : XPathNavigator { private readonly IDocument _document; private INode _currentNode; - private int _attrIndex; private readonly bool _ignoreNamespaces; /// @@ -24,7 +23,6 @@ public HtmlDocumentNavigator(IDocument document, INode currentNode, bool ignoreN _document = document ?? throw new ArgumentNullException(nameof(document)); NameTable = new NameTable(); _currentNode = currentNode ?? throw new ArgumentNullException(nameof(currentNode)); - _attrIndex = -1; _ignoreNamespaces = ignoreNamespaces; } @@ -49,15 +47,28 @@ public HtmlDocumentNavigator(IDocument document, INode currentNode, bool ignoreN /// public override string LocalName => - _attrIndex != -1 - ? NameTable.GetOrAdd(CurrentElement.Attributes[_attrIndex].LocalName) + CurrentNode is IAttr attr + ? attr.LocalName : NameTable.GetOrAdd(CurrentNode is IElement e ? e.LocalName : string.Empty); /// - public override string Name => - _attrIndex != -1 - ? NameTable.GetOrAdd(CurrentElement.Attributes[_attrIndex].Name) - : NameTable.GetOrAdd(_currentNode.NodeName); + public override string Name + { + get + { + if (CurrentNode is IAttr attr) + { + return NameTable.GetOrAdd(attr.Name); + } + + if (CurrentElement != null) + { + return NameTable.GetOrAdd(CurrentElement.LocalName); + } + + return NameTable.GetOrAdd(_currentNode.NodeName); + } + } /// public override string NamespaceURI @@ -69,16 +80,16 @@ public override string NamespaceURI return string.Empty; } - return _attrIndex != -1 - ? NameTable.GetOrAdd(CurrentElement.Attributes[_attrIndex].NamespaceUri ?? string.Empty) + return CurrentNode is IAttr attr + ? NameTable.GetOrAdd(attr.NamespaceUri ?? string.Empty) : NameTable.GetOrAdd(CurrentElement?.NamespaceUri ?? string.Empty); } } /// public override string Prefix => - _attrIndex != 1 - ? NameTable.GetOrAdd(CurrentElement.Attributes[_attrIndex].Prefix ?? string.Empty) + CurrentNode is IAttr attr + ? NameTable.GetOrAdd(attr.Prefix ?? string.Empty) : NameTable.GetOrAdd(CurrentElement?.Prefix ?? string.Empty); /// @@ -107,7 +118,7 @@ public override XPathNodeType NodeType return XPathNodeType.Element; case Dom.NodeType.Element: - return _attrIndex != -1 ? XPathNodeType.Attribute : XPathNodeType.Element; + return XPathNodeType.Element; case Dom.NodeType.ProcessingInstruction: return XPathNodeType.ProcessingInstruction; @@ -155,7 +166,7 @@ public override string Value return documentType.Name; case Dom.NodeType.Element: - return _attrIndex != -1 ? CurrentElement.Attributes[_attrIndex].Value : _currentNode.TextContent; + return _currentNode.TextContent; case Dom.NodeType.Entity: return _currentNode.TextContent; @@ -207,7 +218,6 @@ public override bool MoveTo(XPathNavigator other) if (navigator._document == _document) { _currentNode = navigator._currentNode; - _attrIndex = navigator._attrIndex; return true; } @@ -218,8 +228,8 @@ public override bool MoveTo(XPathNavigator other) public override bool MoveToFirstAttribute() { if (HasAttributes) - { - _attrIndex = 0; + { + _currentNode = CurrentElement.Attributes[0]; return true; } @@ -278,12 +288,24 @@ public override bool MoveToNextAttribute() return false; } - if (_attrIndex >= CurrentElement.Attributes.Length - 1) + if (!(CurrentNode is IAttr attr)) + { + return false; + } + + if (attr.OwnerElement == null) + { + return false; + } + + var attrIndex = attr.OwnerElement.Attributes.Index(attr); + + if (attrIndex >= CurrentElement.Attributes.Length - 1) { - return false; + return false; } - _attrIndex++; + _currentNode = attr.OwnerElement.Attributes[attrIndex + 1]; return true; } @@ -296,6 +318,12 @@ public override bool MoveToNextNamespace(XPathNamespaceScope namespaceScope) /// public override bool MoveToParent() { + if (CurrentNode is IAttr attr) + { + _currentNode = attr.OwnerElement; + return true; + } + if (_currentNode.Parent == null) { return false;