Использование объектной модели Open XML для операций над файлами Excel 2007 и PowerPoint 2007 (часть 1 из 2)

Аннотация. Предварительная версия пакета SDK Microsoft для форматов Open XML — это библиотека доступа к файлам формата Open XML. В этой первой из двух запланированных статей описан код, который с помощью объектной модели Open XML позволяет получать доступ и производить операции над файлами Microsoft Office Excel 2007 и Microsoft Office PowerPoint 2007. (12 печатных страниц).

Фрэнк Райс (Frank Rice), корпорация Майкрософт

Август 2007 г.

Продукты и технологии: Microsoft Office Excel 2007, Microsoft Office PowerPoint 2007

Содержание

  • Обзор

  • Удаление комментариев в книгах по имени пользователя

  • Удаление листов

  • Обнаружение в книгах скрытых столбцов и строк

  • Экспорт диаграмм из книг

  • Получение списка скрытых книг

  • Получение значения ячейки в книге

  • Заключение

  • Дополнительные материалы

Смотрите вторую часть: Использование объектной модели Open XML для операций над файлами Excel 2007 и PowerPoint 2007 (часть 2 из 2).

Обзор

В системе Microsoft Office 2007 впервые использован новый, основанный на XML формат файлов, называемый «форматы Open XML». Microsoft Office Word 2007, Microsoft Office Excel 2007 и Microsoft Office PowerPoint 2007 (русские версии) используют этот формат в качестве формата своих файлов по умолчанию. Форматы Open XML полезны, поскольку являются открытыми стандартами и основываются на хорошо известных технологиях: ZIP и XML. Microsoft предоставляет библиотеку доступа к таким файлам в пространстве имен System.IO.Packaging.NET Framework 3.0 предварительной версии пакета SDK Microsoft для форматов Open XML. Объектная модель Open XML основана на API System.IO.Packaging. Она предоставляет набор классов со строгим контролем типов для операций над документами Open XML. Пакет SDK упрощает задачу управления пакетами Open XML. Решение многих задачи, которые часто встречаются разработчикам, включено в объектную модель Open XML. Благодаря этой модели всего несколькими строками кода можно выполнять сложные операции.

ПримечаниеПримечание.

Дополнительные примеры операций над файлами формата Open XML, а также ссылки на каждый член класса, содержащийся в объектной модели Open XML для 2007 Office System: Пакет SDK Microsoft SDK для форматов Open XML.

Спецификация Open Package Convention определяет набор файлов XML, в которых находится содержимое и определяются отношения всех частей документа, сохранённых в одном пакете. Файлы документов программ выпуска 2007 Microsoft Office, поддерживающих формат Open XML, состоят из комбинаций таких пакетов. Обсуждаемая в этой статье объектная модель Open XML позволяет создавать пакеты и управлять файлами, составляющими пакеты. В этой серии статей я опишу код, с помощью которого можно осуществлять доступ и выполнять операции над пакетами Open XML в Excel 2007 и PowerPoint 2007.

Удаление комментариев в книгах по имени пользователя

Следующий код удаляет из документа комментарии по указанному имени пользователя.

Visual Basic

Public Sub XLDeleteCommentsByUser(ByVal fileName As String, ByVal userName As String)
Const commentsSchema As String = "https://schemas.openxmlformats.org/spreadsheetml/2006/main"
Dim doc As SpreadsheetDocument = SpreadsheetDocument.Open(fileName, True)
'  Manage namespaces to perform XML XPath queries.
Dim nt As NameTable = New NameTable
Dim nsManager As XmlNamespaceManager = New XmlNamespaceManager(nt)
nsManager.AddNamespace("sh", commentsSchema)
For Each part As WorksheetPart In doc.WorkbookPart.WorksheetParts
If (Not (part.WorksheetCommentsPart) Is Nothing) Then
Dim commentDoc As XmlDocument = New XmlDocument
commentDoc.Load(part.WorksheetCommentsPart.GetStream)
Dim searchString As String = String.Format("//sh:authors/sh:author[text()='{0}']", userName)
Dim node As XmlNode = commentDoc.SelectSingleNode(searchString, nsManager)
If (Not (node) Is Nothing) Then
Dim nodes As XmlNodeList = node.SelectNodes("preceding-sibling::sh:author", nsManager)
Dim authorID As Integer = nodes.Count

        Dim commentListNode As XmlNode = commentDoc.SelectSingleNode("sh:comments/sh:commentList", nsManager)
        If (Not (commentListNode) Is Nothing) Then
           searchString = String.Format("./sh:comment[@authorId='{0}']", authorID)
           Dim comments As XmlNodeList = commentListNode.SelectNodes(searchString, nsManager)
           For Each commentNode As System.Xml.XmlNode In comments
              commentListNode.RemoveChild(commentNode)
           Next
        End If
     End If
     '  Save the comment XML back to its part.
     commentDoc.Save(part.WorksheetCommentsPart.GetStream(FileMode.Create))
  End If

Next End Sub

C#FakePre-9e051270848a487da16761e76f16e868-7f8200c6b8a942bc9dc40a9d41c76aef

В эту процедуру передаются два параметра: полный путь к файлу и имя пользователя человека, чьи комментарии необходимо удалить. Затем с помощью метода Open объекта SpreadsheetDocument входной файл открывается как пакет Open XML. Затем устанавливается диспетчер пространства имен, используя объект XmlNamespaceManager и ссылку на пространство имен по умолчанию с квалификатором sh.

Затем в процедуре проверяется наличие комментариев в пакете. Содержимое комментариев загружается в постоянно находящийся в памяти документ XML. Затем в книге производится поиск заданного имени пользователя с помощью выражений Xpath sh:authors/sh:author. Если имя пользователя найдено, нужно узнать порядковый номер имени пользователя. Потом возвращается индекс выбранного узла. Для этого нужно сосчитать количество узлов, предшествующих данному, получая ссылки на все узлы.

В конце работы обновленный поток с комментариями сохраняется в часть комментариев.

Удаление листов

Следующий код удаляет книгу из документа электронной таблицы.

Visual Basic

Public Function XLDeleteSheet(ByVal fileName As String, ByVal sheetToDelete As String) As Boolean
Dim returnValue As Boolean = False
'  Open the package with read/write access.
Dim xlDoc As SpreadsheetDocument = SpreadsheetDocument.Open(fileName, True)
Dim doc As XmlDocument = New XmlDocument
doc.Load(xlDoc.WorkbookPart.GetStream)
Dim nsManager As XmlNamespaceManager = New XmlNamespaceManager(doc.NameTable)
nsManager.AddNamespace("d", doc.DocumentElement.NamespaceURI)
Dim searchString As String = String.Format("//d:sheet[@name='{0}']", sheetToDelete)
Dim node As XmlNode = doc.SelectSingleNode(searchString, nsManager)
If (Not (node) Is Nothing) Then
Dim relationAttribute As XmlAttribute = node.Attributes("r:id")
If (Not (relationAttribute) Is Nothing) Then
Dim relId As String = relationAttribute.Value
xlDoc.WorkbookPart.DeletePart(relId)
node.ParentNode.RemoveChild(node)
doc.Save(xlDoc.WorkbookPart.GetStream(FileMode.Create))
returnValue = True
End If
End If
Return returnValue
End Function
C#FakePre-5b9e78900def4d038c67f298bcf89165-7f268e467943420f85fa627e74ba8c78

В эту процедуру передаются два параметра: полный путь к книге и имя листа, который надо удалить. Затем с помощью метода Open объекта SpreadsheetDocument входной файл открывается как пакет Open XML. Потом содержимое книги загружается в документ XML DOM. Затем устанавливается диспетчер пространства имен, используя объект XmlNamespaceManager и ссылку на пространство имен по умолчанию с квалификатором d. Найти указанный лист в документе можно, используя атрибут «имя» узла //d:sheet.

Для каждого найденного узла, если такие узлы существуют, извлекается код связи и лист, соответствующий этому коду, удаляется.

В конце обновленные исправления SpreadsheetML сохраняются назад, в главную часть книги.

Обнаружение в книгах скрытых столбцов и строк

По заданному имени книги и листа данный код возвращает список номеров скрытых строк или скрытых столбцов.

Visual Basic

Public Function XLDetectHiddenRowsOrCols(ByVal fileName As String, ByVal sheetName As String, ByVal detectRows As Boolean) As List(Of Integer)
Const worksheetSchema As String = "https://schemas.openxmlformats.org/spreadsheetml/2006/main"
'  Set up the return value list.
Dim itemList As List(Of Integer) = New System.Collections.Generic.List(Of Integer)
Dim xlDoc As SpreadsheetDocument = SpreadsheetDocument.Open(fileName, False)
'  Load the contents of the workbook.
Dim doc As XmlDocument = New XmlDocument
doc.Load(xlDoc.WorkbookPart.GetStream)
'  Create a namespace manager, so you can search.
'  Add a prefix for the default namespace.
Dim nsManager As XmlNamespaceManager = New XmlNamespaceManager(doc.NameTable)
nsManager.AddNamespace("d", worksheetSchema)
Dim searchString As String = String.Format("//d:sheet[@name='{0}']", sheetName)
Dim sheetNode As XmlNode = doc.SelectSingleNode(searchString, nsManager)
If (Not (sheetNode) Is Nothing) Then
'  Get the relId attribute.
Dim relationAttribute As XmlAttribute = sheetNode.Attributes("r:id")
If (Not (relationAttribute) Is Nothing) Then
Dim relId As String = relationAttribute.Value
Dim sheetPart As WorksheetPart = CType(xlDoc.WorkbookPart.GetPartById(relId), WorksheetPart)
'  First, get the relationship between the document and the sheet.
'  Load the contents of the workbook.
Dim sheetDoc As XmlDocument = New XmlDocument
sheetDoc.Load(sheetPart.GetStream(FileMode.Open))
If detectRows Then
'  Retrieve the list of hidden rows.
For Each node As System.Xml.XmlNode In sheetDoc.SelectNodes("//d:row[@hidden='1']", nsManager)
'  For each hidden row, add information to the output list.
Dim rowAttr As XmlAttribute = node.Attributes("r")
If (Not (rowAttr) Is Nothing) Then
itemList.Add(Convert.ToInt32(rowAttr.Value))
End If
Next
Else
'  Retrieve the list of hidden columns
For Each node As System.Xml.XmlNode In sheetDoc.SelectNodes("//d:cols/d:col", nsManager)
Dim hiddenAttr As XmlAttribute = node.Attributes("hidden")
If (Not (hiddenAttr) Is Nothing) Then
If (hiddenAttr.Value = "1") Then
'  Get the range of columns that are hidden.
Dim minAttr As XmlAttribute = node.Attributes("min")
Dim maxAttr As XmlAttribute = node.Attributes("max")
If ((Not (minAttr) Is Nothing) AndAlso (Not (maxAttr) Is Nothing)) Then
'  Finally, add the range of values to the list.
Dim minValue As Integer = Convert.ToInt32(minAttr.Value)
Dim maxValue As Integer = Convert.ToInt32(maxAttr.Value)
Dim i As Integer = minValue
Do While (i <= maxValue)
itemList.Add(i)
i = (i + 1)
Loop
End If
End If
End If
Next
End If
End If
End If
Return itemList
End Function
C#FakePre-ab0fabd9cd3e41108d2fee3cad35c729-8a18cbe20cc5442aae23a6873a89e678

В эту процедуру передаются три параметра: полный путь к книге Excel 2007, имя листа и логическое значение, указывающее, искать ли скрытые строки. Если detectRows равен истине, процедура возвращает список скрытых строк. Если detectRows равен лжи, возвращается список скрытых столбцов. Затем с помощью метода Open объекта SpreadsheetDocument входной файл открывается как пакет Open XML. Потом содержимое книги загружается в документ XML DOM. Затем устанавливается диспетчер пространства имен, используя объект XmlNamespaceManager и ссылку на пространство имен по умолчанию с квалификатором d. Найти узел, представляющий указанный лист в документе можно, используя атрибут «имя» узла //d:sheet.

Если узел найден, то отношение между книгой и листом получено. Сначала загружается содержимое листа. Если detectRows равен истина, то список скрытых строк получается использованием атрибута «скрытый» узла //d:row.

ПримечаниеПримечание.

Строки нумеруются, начиная с 1. Столбцы нумеруются, начиная с нуля (0).

Если detectRows равен ложь , то производится поиск скрытых столбцов с использованием атрибута «скрытый» узла //d:cols/d:col.

И наконец, список элементов возвращается вызывающей процедуре.

Экспорт диаграмм из книг

Для заданной книги Excel 2007 и названия диаграммы создать файл Word 2007 и экспортировать диаграмму.

Visual Basic

Public Sub XLExportChart(ByVal inputFileName As String, ByVal outputFileName As String, ByVal chartTitle As String)
Const drawingMLSchema As String = "https://schemas.openxmlformats.org/drawingml/2006/main"
Const chartMLSchema As String = "https://schemas.openxmlformats.org/drawingml/2006/chart"
Dim chartFound As Boolean = False
Dim xlDoc As SpreadsheetDocument = SpreadsheetDocument.Open(inputFileName, False)
For Each sheetPart As WorksheetPart In xlDoc.WorkbookPart.WorksheetParts
If (sheetPart.DrawingsPart Is Nothing) Then
Continue For
End If
'  Loop through all the parts related to the worksheet.
For Each chartPart As ChartPart In sheetPart.DrawingsPart.ChartParts
'  A chart is found. Does it have the correct title?
'  Create a namespace manager, so you can search.
Dim nt As NameTable = New NameTable
Dim nsManager As XmlNamespaceManager = New XmlNamespaceManager(nt)
nsManager.AddNamespace("c", chartMLSchema)
nsManager.AddNamespace("a", drawingMLSchema)
'  Load the chart document from the part's stream, and
'  search for the requested title.
Dim chartDoc As XmlDocument = New XmlDocument(nt)
chartDoc.Load(chartPart.GetStream)
Dim titleNode As XmlNode = chartDoc.SelectSingleNode("//c:chart//c:title//a:t", nsManager)
If (Not (titleNode) Is Nothing) Then
If (String.Compare(titleNode.InnerText, chartTitle, True) = 0) Then
Dim newDoc As WordprocessingDocument = WordprocessingDocument.Create(outputFileName, WordprocessingDocumentType.Document)
newDoc.AddMainDocumentPart()
newDoc.MainDocumentPart.AddPart(Of ChartPart)(chartPart)
'  Tell the outer loops that you are finished.
chartFound = True
End If
End If
Next
'  Because you need to export only a single chart, exit.
If chartFound Then
Exit Sub
End If
Next
End Sub
C#FakePre-6f3a7bb75ce54755802941a4a0f09431-a2095d899052415885ad9896463bd4d2

В эту процедуру передаются три параметра: полный путь к книге, полный путь к выходному файлу и имя диаграммы.

ПримечаниеПримечание.

В этой процедуре документ Word 2007 используется как временное хранилище для диаграммы, поэтому отношения не определяются. В результате документ нельзя открыть в Word. Код этой процедуры можно заменить кодом, который правильно добавляет часть к пакету документа. Образцы кода такого типа находятся здесь: 2007 Office System: пакет SDK Microsoft SDK для форматов Open XML.

Сначала с помощью метода Open объекта SpreadsheetDocument входной файл открывается как пакет Open XML. Затем устанавливается диспетчер пространства имен, используя объект XmlNamespaceManager и ссылку на пространство имен по умолчанию chartML с квалификатором с и

После этого процедура в цикле ищет диаграмму с заданным названием по всем частям chartPart, используя выражение XPath //c:chart//c:title//a:t. Если такое выражение находится, то создается документ Word 2007 и в его пакет экспортируется часть chartPart.

Получить списки скрытых листов

Следующий код содержит создание списка всех скрытых листов в заданной книге.

Visual Basic

Public Function XLGetHiddenSheets(ByVal fileName As String) As List(Of String)
Dim sheets As List(Of String) = New List(Of String)()
Dim xlDoc As SpreadsheetDocument = SpreadsheetDocument.Open(fileName, False)
'  Get the main document part (workbook.xml).
Dim doc As XmlDocument = New XmlDocument
doc.Load(xlDoc.WorkbookPart.GetStream)
'  Create a NamespaceManager to handle the default namespace,
'  and create a prefix for the default namespace.
Dim nsManager As XmlNamespaceManager = New XmlNamespaceManager(doc.NameTable)
nsManager.AddNamespace("default", doc.DocumentElement.NamespaceURI)
For Each node As System.Xml.XmlNode In doc.SelectNodes("//default:sheets//default:sheet[@state='hidden']", nsManager)
Dim sheetName As String = node.Attributes("name").Value
sheets.Add(sheetName)
Next
Return sheets
End Function
C#FakePre-6f6d194990a9430282c30aef4549e51a-61b7e1817d274bbdaff23ac74c345094

В эту процедуру передается один параметр: полный путь к книге. Затем с помощью метода Open объекта SpreadsheetDocument входной файл открывается как пакет Open XML и данные загружаются в XML-документ. Затем устанавливается диспетчер пространства имен, используя объект XmlNamespaceManager и ссылку на пространство имен по умолчанию SpreadsheetML для поиска скрытых листов.

Поиск скрытых листов в документе выполняется с использованием атрибута узла //default:sheets//default:sheet. Имя каждого найденного скрытого листа добавляется в список листов. После того как все листы просмотрены, список листов возвращается вызвавшей процедуре.

Получение значения ячейки в листах

Следующий код возвращает значение ячейки в листе.

Visual Basic

Public Function XLGetCellValue(ByVal fileName As String, ByVal sheetName As String, ByVal addressName As String) As String
Const worksheetSchema As String = "https://schemas.openxmlformats.org/spreadsheetml/2006/main"
Const sharedStringSchema As String = "https://schemas.openxmlformats.org/spreadsheetml/2006/main"
Dim cellValue As String = Nothing
'  Retrieve the stream containing the requested
'  worksheet's information.
Dim xlDoc As SpreadsheetDocument = SpreadsheetDocument.Open(fileName, False)
'  Get the main document part (workbook.xml).
Dim doc As XmlDocument = New XmlDocument
doc.Load(xlDoc.WorkbookPart.GetStream)
'  Create a namespace manager, so you can search.
'  Add a prefix (d) for the default namespace.
Dim nt As NameTable = New NameTable
Dim nsManager As XmlNamespaceManager = New XmlNamespaceManager(nt)
nsManager.AddNamespace("d", worksheetSchema)
nsManager.AddNamespace("s", sharedStringSchema)
Dim searchString As String = String.Format("//d:sheet[@name='{0}']", sheetName)
Dim sheetNode As XmlNode = doc.SelectSingleNode(searchString, nsManager)
If (Not (sheetNode) Is Nothing) Then
'  Get the relId attribute.
Dim relationAttribute As XmlAttribute = sheetNode.Attributes("r:id")
If (Not (relationAttribute) Is Nothing) Then
Dim relId As String = relationAttribute.Value
'  Load the contents of the workbook.
Dim sheetDoc As XmlDocument = New XmlDocument(nt)
sheetDoc.Load(xlDoc.WorkbookPart.GetPartById(relId).GetStream)
Dim cellNode As XmlNode = sheetDoc.SelectSingleNode(String.Format("//d:sheetData/d:row/d:c[@r='{0}']", addressName), nsManager)
If (Not (cellNode) Is Nothing) Then
Dim typeAttr As XmlAttribute = cellNode.Attributes("t")
Dim cellType As String = String.Empty
If (Not (typeAttr) Is Nothing) Then
cellType = typeAttr.Value
End If
Dim valueNode As XmlNode = cellNode.SelectSingleNode("d:v", nsManager)
If (Not (valueNode) Is Nothing) Then
cellValue = valueNode.InnerText
End If
If (cellType = "b") Then
If (cellValue = "1") Then
cellValue = "TRUE"
Else
cellValue = "FALSE"
End If
ElseIf (cellType = "s") Then
If (Not (xlDoc.WorkbookPart.SharedStringTablePart) Is Nothing) Then
Dim stringDoc As XmlDocument = New XmlDocument(nt)
stringDoc.Load(xlDoc.WorkbookPart.SharedStringTablePart.GetStream)
'  Add the string schema to the namespace manager.
nsManager.AddNamespace("s", sharedStringSchema)
Dim requestedString As Integer = Convert.ToInt32(cellValue)
Dim strSearch As String = String.Format("//s:sst/s:si[{0}]", (requestedString + 1))
Dim stringNode As XmlNode = stringDoc.SelectSingleNode(strSearch, nsManager)
If (Not (stringNode) Is Nothing) Then
cellValue = stringNode.InnerText
End If
End If
End If
End If
End If
End If
Return cellValue
End Function
C#FakePre-5b912e2300404baca7ac5055322b6869-1bd8b9082cc745e59dc03537ee55e81a

В эту процедуру передаются три параметра: полный путь к книге, имя листа и адрес ячейки, величину которой нужно получить. Затем с помощью метода Open объекта SpreadsheetDocument входной файл открывается как пакет Open XML и данные загружаются в XML-документ. Затем устанавливается диспетчер пространства имен, используя объект XmlNamespaceManager и ссылку на пространство имен по умолчанию worksheetSchema с квалификатором d и на пространство имен sharedStringSchema с квалификатором s. Пространство имен sharedStringSchema ссылается на часть SharedStringTablePart part, содержащую строки, разделяемые между клетками.

Потом по атрибуту «имя» узла //d:sheet извлекается узел, представляющий указанный лист в главной книге. Если узел найден, то из листа извлекается значение кода связи, которое используется для загрузки листа в документ XML. Затем извлекается значение. Если в узле атрибут t содержит s, значит, это разделяемая строка, и ее значение нужно искать в части SharedStringTablePart. Иначе значение получается непосредственно из узла.

ПримечаниеПримечание.

Этот код проверяет индивидуально только логические и строковые величины.

Наконец, процедура возвращает или значение ячейки, или логическое значение, указывающее на результат поиска**.**

Заключение

Как показано в этой статье, работа с файлами Excel 2007 и PowerPoint 2007 намного легче предварительной версией пакета SDK Microsoft для форматов Open XML. Во второй части этой серии статей (которая скоро выйдет) я опишу другие общие действия, которые можно производить с файлами Excel 2007 и PowerPoint 2007.

Дополнительные источники

Для получения дополнительных сведений можно использовать следующие материалы.

Скоро выйдет

  • Использование объектной модели Open XML для операций над файлами Excel 2007 и PowerPoint 2007 (часть 2 из 2)