using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public static partial class HtmlAgilityPackExtensions
public static HtmlNode LowestCommonNode(this HtmlNode node1, HtmlNode node2)
if (node1 == null || node2 == null)
throw new ArgumentNullException();
if (node1.OwnerDocument != node2.OwnerDocument)
throw new ArgumentException();
return node1.AncestorsAndSelf().Reverse().Zip(node2.AncestorsAndSelf().Reverse()).Last(p => p.Item1 == p.Item2).Item1;
public static partial class HtmlAgilityPackExtensions
public static IEnumerable<HtmlNode> NextNodes(this HtmlNode start, bool includeSelf = false)
var current = start.FirstChild;
var next = current.FirstChild;
next = current.NextSibling;
for (var parent = current.ParentNode; parent != null && next == null; parent = parent.ParentNode)
next = parent.NextSibling;
public static IEnumerable<HtmlNode> NodesUntil(this HtmlNode start, HtmlNode end, bool includeSelf = false)
if (start == null || end == null)
throw new ArgumentNullException();
if (start.OwnerDocument != end.OwnerDocument)
throw new ArgumentException();
var query = start.NextNodes(includeSelf).TakeWhile(n => n != end);
query = query.Concat(new [] { end });
public static void Test()
var doc = new HtmlDocument();
Assert.IsTrue(doc.DocumentNode.DescendantsAndSelf().SequenceEqual(doc.DocumentNode.NextNodes(true)));
Assert.IsTrue(doc.DocumentNode.Descendants().SequenceEqual(doc.DocumentNode.NextNodes(false)));
var startnode = doc.DocumentNode.SelectNodes("//h2").Where(n => n.InnerText == "Applicant").FirstOrDefault();
var endnode = doc.DocumentNode.SelectNodes("//h2").Where(n => n.InnerText == "Agent").FirstOrDefault();
var query = doc.DocumentNode.Descendants()
.SkipWhile(n => n != startnode)
.TakeWhile(n => n != endnode);
Console.WriteLine(string.Format("{0} nodes found between {1} and {2} using DocumentNode.Descendants()", query.Count(), startnode.XPath, endnode.XPath));
var query2 = startnode.NodesUntil(endnode, false);
Assert.IsTrue(query.SequenceEqual(query2));
Console.WriteLine("HtmlAgilityPackExtensions.NextNodes() returned identically.");
static string GetHtml() =>
<body class=""some example"">
<div id=""main-container-z"">
<!-- Applicants section -->
<h2 class=""GridTitle"">Applicant</h2>
<h3 class=""DataTitle"">1</h3>
<dl class=""Grid LeftCol"">
<dl class=""Grid RightCol"">
<dd>Some address here</dd>
<h3 class=""DataTitle"">2</h3>
<dl class=""Grid LeftCol"">
<dl class=""Grid RightCol"">
<dd>Some address here1</dd>
<h2 class=""GridTitle"">Agent</h2>
public static void Main()
Console.WriteLine("Environment version: {0}", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription);
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("HtmlAgilityPack version: " + typeof(HtmlDocument).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");