Переходы по дереву DOM-модели Open XML

В последних записях я уделял внимание демонстрации решений для реальных, повседневных сценариев. Сегодня я сменю темп и продолжу обсуждение Али, посвященное основным сведениям о пакете Open XML SDK. В этой записи мы рассмотрим основные методы перехода по дереву DOM-модели Open XML с помощью пакета Open XML SDK.

И снова, это всего лишь XML...

Мы разработали DOM-модель низкого уровня для Open XML в качестве XML-оболочки схем Open XML. Иными словами, этот компонент пакета SDK позволяет работать непосредственно со строго типизированными объектами и классами, представляющими основные узлы XML. По сути, даже используя SDK, вы продолжаете работать только с XML.

Существует несколько традиционных технологий, позволяющих выполнять переход на более низкий уровень дерева XML. При разработке Open XML SDK мы хотели включить в пакет функции DOM и LINQ, чтобы предоставить пользователям богатый набор средств. Компонент DOM-модели низкого уровня — это попытка объединить концепции API-интерфейса DOM-модели .NET и LINQ в XML. Рассмотрим некоторые из этих концепций.

Переход на более низкий уровень дерева XML

Одной из наиболее распространенных задач при чтении XML-файлов является переход на более низкий уровень конкретного узла XML, например корневого. Обычно переход вниз требуется при поиске. Допустим, вам необходимо изменить содержимое всех строк таблицы в документе. Одним их способов выполнения такого сценария является переход на более низкий уровень, начиная с корневого элемента документа с нахождением таблицы и элементов строк таблицы до тех пор, пока не будет достигнут конец документа. Open XML SDK упрощает решение этой задачи.

Как уже упоминалось в записи Али, все элементы Open XML основываются на абстрактном классе OpenXMLElement. Класс OpenXMLElement предоставляет следующие методы и свойства для перехода вниз в пределах DOM-модели:

  1. OpenXMLElement FirstChild { get; }
  2. OpenXMLElement LastChild { get; }
  3. IEnumerable<OpenXmlElement> Elements ()
  4. OpenXmlElementList ChildElements { get; }.
  5. IEnumerable<OpenXmlElement> GetEnumerator ()
  6. IEnumerable<OpenXmlElement> Descendants ()

Между перечисленными подходами существуют некоторые ключевые различия. Для более наглядного представления этих различий представьте, что вам необходимо выполнить чтение элементов в объекте Table, который содержит свойства таблицы, сведения о сетке таблицы и содержимое строк:

<w:tbl>

<w:tblPr>

...

</w:tblPr>

<w:tblGrid>

...

</w:tblGrid>

<w:tr>

...

</w:tr>

...

<w:tr>

...

</w:tr>

</w:tbl>

Методы FirstChild() и LastChild() довольно прямолинейны. Они возвращают первый и последний дочерние элементы соответственно.

Метод Elements() позволяет выполнять чтение всех дочерних элементов объекта Table, используя следующий фрагмент кода:

foreach (OpenXmlElement el in tbl.Elements())

{

//DO SOMETHING

}

Свойство ChildElements немного отличается от остальных. При его непосредственном вызове, подобно tbl.ChildElements, оно возвращает список элементов Open XML, являющихся дочерними элементами таблицы. В этом отношении оно эквивалентно свойству tbl.Elements(). Отличие свойства ChildElements от Elements() заключается в возможности указания индекса. Например, если требуется четвертый дочерний элемент объекта Table, вы вызываете свойство tbl.ChildElements[3].

Подобно двум другим подходам, метод GetEnumerator() обеспечивает поддержку итерации по всем дочерним элементам.

Метод Descendants(), наоборот, позволяет выполнять итерацию по всем дочерним элементам и потомкам конкретного узла. Используя объект Table в качестве примера и вызвав метод Descendants(), можно не только просмотреть строки таблицы, но и ячейки внутри строк.

Вместо выполнения итерации по всем дочерним элементам более распространенный сценарий выполняет поиск дочерних элементов или потомков конкретного типа класса. Допустим, необходимо найти элементы строки таблицы под объектом Table. Вместо выполнения итерации по всем дочерним элементам с проверкой, является ли каждый из них строкой таблицы, можно просто воспользоваться следующими методами:

  1. IEnumerable<T> Elements<T> ()where T : OpenXmlElement
  2. IEnumerable<T> Descendants<T> ()where T : OpenXmlElement
  3. T GetFirstChild<T> ()where T : OpenXmlElement

Первые два метода возвращают только элементы типа T или производные от T. Для поиска в приведенном выше примере всех строк таблицы необходимо использовать следующий фрагмент кода:

foreach (TableRow tr in tbl.Elements<TableRow>())

{

//DO SOMETHING

}

Последний метод позволяет получить первый дочерний элемент, принадлежащий заданному типу. В данном случае для получения первой строки я бы использовал tbl.GetFirstChild<TableRow>().

Переход на более высокий уровень дерева XML

Подобно переходу на более низкий уровень дерева XML можно переходить и на более высокий. Эта функция обеспечивает гибкость при переходе по дереву DOM-модели. Допустим, вы выполняете поиск конкретного текста и вам необходимо получить дополнительные сведения о его контексте, например содержится ли текст в таблице. Для выполнения этого сценария необходимо выполнить переход вверх по дереву. Пакет Open XML SDK предоставляет следующие методы и свойства для перехода на более высокий уровень в DOM-модели:

  1. OpenXmlElement Parent
  2. IEnumerable<OpenXmlElement> Ancestors ()
  3. IEnumerable< T > Ancestors<T> () where T : OpenXmlElement

Свойство Parent возвращает непосредственный родительский элемент конкретного узла. При вызове свойства Parent для объекта строки таблицы возвращается объект Table.

Методы Ancestors() действуют подобно методам Descendants(), кроме того, что выполняют переход вверх, а не вниз.

Переход по горизонтали в пределах дерева XML

Что делать, если необходимо выполнить переход по дереву XML по горизонтали с просмотром одноуровневых элементов? Open XML SDK может справиться и с этим. В пакете Open XML SDK предусмотрены следующие методы для выполнения таких сценариев:

  1. OpenXmlElement PreviousSibling ()
  2. T PreviousSibling<T> () where T : OpenXmlElement
  3. OpenXmlElement NextSibling ()
  4. T NextSibling<T> () where T : OpenXmlElement
  5. IEnumerable<OpenXmlElement> ElementsBefore ()
  6. IEnumerable<OpenXmlElement> ElementsAfter ()

Первые четыре метода возвращают ближайшие элементы того же уровня, расположенные до или после текущего элемента, а остальные методы перечисляют все одноуровневые элементы, расположенные до или после элемента под тем же родительским элементом.

Заключение

Надеюсь, в этой публикации мне удалось продемонстрировать некоторые распространенные способы перехода по дереву DOM-модели Open XML. Благодаря этим функциям вы можете выполнять поиск необходимых элементов, используя всего несколько строк кода.

Зияд Раджаби (Zeyad Rajabi)

Это локализованная запись блога. Исходную статью можно найти по адресу https://blogs.msdn.com/brian_jones/archive/2009/01/28/traversing-in-the-open-xml-dom.aspx.