пятница, 29 апреля 2011 г.

Допиливаем HtmlAgilityPack. Часть 2

У класса HtmlNode есть свойство NextSibling, возращающее следующий элемент того же уровня (далее - сиблинг). Однако зачастую нужно получить следующий сиблинг с определенным тегом. Даже в самой простой ситуации:

anchor1
NextSibling для a1 вернет пробельный символ, а не a2. Поэтому я посчитал целесообразным добавить метод NextSiblingWithName (предложите лучшее название):

public HtmlNode NextSiblingWithName(string name)
{
var node = _nextnode;
while (node != null)
{
if (node.Name == name)
return node;

node = node.NextSibling;
}

return node;
}

Следующий повод для расширения дал метод SelectNodes, которого мы уже касались в первой части. Часто при использовании SelectNodes нужно проверить количество выбранных нод. Например:

var nodes = document.DocumentNode.SelectNodes("xpath query");
if (nodes.Count == 0)
throw new Exception("Expected at least one node");

Для того чтобы не захламлять код парсеров подобными проверками, я написал ряд методов - расширений:

public static List CountIsEqualTo(this List list, int count)
{
if (list.Count != count)
throw new Exception(String.Format("Expected count = {0} but was {1}", count, list.Count));

return list;
}

public static List CountIsNotEqualTo(this List list, int count)
{
if (list.Count == count)
throw new Exception(String.Format("Expected count != {0} but was {1}", count, list.Count));

return list;
}

public static List CountIsNotZero(this List list)
{
return CountIsNotEqualTo(list, 0);
}

public static List CountIsMoreThan(this List list, int count)
{
if (list.Count <= count)
throw new Exception(String.Format("Expected count > {0} but was {1}", count, list.Count));

return list;
}

public static List CountIsEqualOrMoreThan(this List list, int count)
{
if (list.Count < count)
throw new Exception(String.Format("Expected count >= {0} but was {2}", count, list.Count));

return list;
}

Самый популярный метод в моих парсерах - CountIsNotZero:

var nodes = document.DocumentNode.SelectNodes("xpath query").CountIsNotZero();

Отмечу, что для случаев, когда возвращается один элемент, все же удобнее использовать методы SelectSingleNode, SelectSingleNodeOrDefault, SelectFirstNode, SelectFirstNodeOrDefault, которые мы реализовали в первой части.

Комментариев нет: