Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Thema wird gezeigt, wie Sie das Open XML SDK für Office verwenden, um ein großes Arbeitsblatt mithilfe von SAX (Simple API for XML) programmgesteuert zu kopieren. Weitere Informationen zur grundlegenden Struktur eines SpreadsheetML
Dokuments finden Sie unter Struktur eines SpreadsheetML-Dokuments.
Gründe für die Verwendung des SAX-Ansatzes
Das Open XML SDK bietet zwei Möglichkeiten zum Analysieren von Office Open XML-Dateien: das Dokumentobjektmodell (DOM) und die Simple API for XML (SAX). Der DOM-Ansatz wurde entwickelt, um das Abfragen und Analysieren von Open XML-Dateien mithilfe von stark typisierten Klassen zu vereinfachen. Der DOM-Ansatz erfordert jedoch das Laden ganzer Open XML-Teile in den Arbeitsspeicher, was bei der Arbeit mit sehr großen Teilen zu einer langsameren Verarbeitung und Out of Memory
Ausnahmen führen kann.
Der SAX-Ansatz liest den XML-Code in einem Open XML-Teil jeweils ein Element ein, ohne den gesamten Teil in den Arbeitsspeicher zu lesen, sodass nicht zwischengespeicherte Vorwärtszugriffe auf XML-Daten möglich sind. Dies macht es zu einer besseren Wahl beim Lesen sehr großer Teile, z. B. eines WorksheetPart mit Hunderttausenden von Zeilen.
Verwenden des DOM-Ansatzes
Mit dem DOM-Ansatz können wir die stark typisierten Klassen des Open XML SDK nutzen. Der erste Schritt besteht darin, auf die des Pakets WorksheetPart
zuzugreifen und sicherzustellen, dass es nicht NULL ist.
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(path, true))
{
// Get the first sheet
WorksheetPart? worksheetPart = spreadsheetDocument.WorkbookPart?.WorksheetParts?.FirstOrDefault();
if (worksheetPart is not null)
Nachdem festgestellt wurde, dass der WorksheetPart
zu kopierende nicht NULL ist, fügen Sie eine neue WorksheetPart
hinzu, in die sie kopiert werden soll.
Klonen Sie dann das WorksheetPart
-Objekt von , Worksheet und weisen Sie das geklonte Worksheet
der neuen WorksheetPart
Worksheet-Eigenschaft zu.
// Add a new WorksheetPart
WorksheetPart newWorksheetPart = spreadsheetDocument.WorkbookPart!.AddNewPart<WorksheetPart>();
// Make a copy of the original worksheet
Worksheet newWorksheet = (Worksheet)worksheetPart.Worksheet.Clone();
// Add the new worksheet to the new worksheet part
newWorksheetPart.Worksheet = newWorksheet;
An diesem Punkt wurde das neue WorksheetPart
hinzugefügt, aber ein neues Sheet Element muss den untergeordneten Elementen des WorkbookPart
-Elements Sheetshinzugefügt werden, damit es angezeigt werden kann. Suchen Sie dazu zunächst die neue WorksheetPart
ID von , und erstellen Sie eine neue Blatt-ID, indem Sie die Sheets
Anzahl um eins erhöhen, und fügen Sie dann ein neues Sheet
untergeordnetes Element an das Sheets
Element an. Damit wird der Datei das kopierte Arbeitsblatt hinzugefügt.
// Find the new WorksheetPart's Id and create a new sheet id
string id = spreadsheetDocument.WorkbookPart.GetIdOfPart(newWorksheetPart);
uint newSheetId = (uint)(sheets!.ChildElements.Count + 1);
// Append a new Sheet with the WorksheetPart's Id and sheet id to the Sheets element
sheets.AppendChild(new Sheet() { Name = "My New Sheet", SheetId = newSheetId, Id = id });
Verwenden des SAX-Ansatzes
Der SAX-Ansatz funktioniert auf Teilen, sodass der erste Schritt bei Verwendung des SAX-Ansatzes derselbe ist. Greifen Sie auf die des Pakets WorksheetPart zu, und stellen Sie sicher, dass es nicht NULL ist.
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(path, true))
{
// Get the first sheet
WorksheetPart? worksheetPart = spreadsheetDocument.WorkbookPart?.WorksheetParts?.FirstOrDefault();
if (worksheetPart is not null)
Mit SAX haben wir keinen Zugriff auf die Clone -Methode. Beginnen Sie stattdessen mit dem Hinzufügen eines neuen WorksheetPart
zu .WorkbookPart
WorksheetPart newWorksheetPart = spreadsheetDocument.WorkbookPart!.AddNewPart<WorksheetPart>();
Erstellen Sie dann eine instance mit OpenXmlPartReader dem ursprünglichen Arbeitsblattteil und eine instance des OpenXmlPartWriter mit dem neu erstellten Arbeitsblattteil.
using (OpenXmlReader reader = OpenXmlPartReader.Create(worksheetPart))
using (OpenXmlWriter writer = OpenXmlPartWriter.Create(newWorksheetPart))
Lesen Sie dann die Elemente nacheinander mit der Read -Methode. Wenn das Element ein CellValue ist, muss der innere Text explizit mithilfe der GetText -Methode hinzugefügt werden, um den Text zu lesen, da der WriteStartElement den inneren Text eines Elements nicht schreibt. Für andere Elemente müssen wir nur die WriteStartElement
-Methode verwenden, da wir den inneren Text des anderen Elements nicht benötigen.
// Write the XML declaration with the version "1.0".
writer.WriteStartDocument();
// Read the elements from the original worksheet part
while (reader.Read())
{
// If the ElementType is CellValue it's necessary to explicitly add the inner text of the element
// or the CellValue element will be empty
if (reader.ElementType == typeof(CellValue))
{
if (reader.IsStartElement)
{
writer.WriteStartElement(reader);
writer.WriteString(reader.GetText());
}
else if (reader.IsEndElement)
{
writer.WriteEndElement();
}
}
// For other elements write the start and end elements
else
{
if (reader.IsStartElement)
{
writer.WriteStartElement(reader);
}
else if (reader.IsEndElement)
{
writer.WriteEndElement();
}
}
}
An diesem Punkt wurde der Arbeitsblattteil in den neu hinzugefügten Teil kopiert, aber wie beim DOM-Ansatz müssen wir dem -Element des Workbook
Sheets
-Elements trotzdem eine Sheet
hinzufügen. Da der SAX-Ansatz nicht zwischengespeicherten Vorwärtszugriff auf XML-Daten ermöglicht, ist es nur möglich, untergeordnete Elemente voran zu stellen, die in diesem Fall das neue Arbeitsblatt am Anfang und nicht am Ende hinzufügen und die Reihenfolge der Arbeitsblätter ändern würden. Daher ist hier der DOM-Ansatz erforderlich, da wir das neue Sheet
nicht voranstellen möchten und da der WorkbookPart
normalerweise nicht ein großer Teil ist, wären die Leistungssteigerungen minimal.
Sheets? sheets = spreadsheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>();
if (sheets is null)
{
spreadsheetDocument.WorkbookPart.Workbook.AddChild(new Sheets());
}
string id = spreadsheetDocument.WorkbookPart.GetIdOfPart(newWorksheetPart);
uint newSheetId = (uint)(sheets!.ChildElements.Count + 1);
sheets.AppendChild(new Sheet() { Name = "My New Sheet", SheetId = newSheetId, Id = id });
Beispielcode
Im Folgenden finden Sie den Beispielcode für den DOM- und den SAX-Ansatz, um die Daten von einem Blatt in ein neues zu kopieren und zum Tabellenkalkulationsdokument hinzuzufügen. Während der DOM-Ansatz einfacher ist und in vielen Fällen die bevorzugte Wahl ist, ist der SAX-Ansatz bei sehr großen Dokumenten besser, da er schneller ist und Ausnahmen verhindern Out of Memory
kann. Um den Unterschied zu erkennen, erstellen Sie ein Tabellenkalkulationsdokument mit vielen (mehr als 10.000) Zeilen, und überprüfen Sie die Ergebnisse von , Stopwatch um die Differenz in der Ausführungszeit zu überprüfen. Erhöhen Sie die Anzahl der Zeilen auf mehr als 100.000, um noch größere Leistungssteigerungen zu erzielen.
DOM-Ansatz
void CopySheetDOM(string path)
{
Console.WriteLine("Starting DOM method");
Stopwatch sw = new();
sw.Start();
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(path, true))
{
// Get the first sheet
WorksheetPart? worksheetPart = spreadsheetDocument.WorkbookPart?.WorksheetParts?.FirstOrDefault();
if (worksheetPart is not null)
{
// Add a new WorksheetPart
WorksheetPart newWorksheetPart = spreadsheetDocument.WorkbookPart!.AddNewPart<WorksheetPart>();
// Make a copy of the original worksheet
Worksheet newWorksheet = (Worksheet)worksheetPart.Worksheet.Clone();
// Add the new worksheet to the new worksheet part
newWorksheetPart.Worksheet = newWorksheet;
Sheets? sheets = spreadsheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>();
if (sheets is null)
{
spreadsheetDocument.WorkbookPart.Workbook.AddChild(new Sheets());
}
// Find the new WorksheetPart's Id and create a new sheet id
string id = spreadsheetDocument.WorkbookPart.GetIdOfPart(newWorksheetPart);
uint newSheetId = (uint)(sheets!.ChildElements.Count + 1);
// Append a new Sheet with the WorksheetPart's Id and sheet id to the Sheets element
sheets.AppendChild(new Sheet() { Name = "My New Sheet", SheetId = newSheetId, Id = id });
}
}
sw.Stop();
Console.WriteLine($"DOM method took {sw.Elapsed.TotalSeconds} seconds");
}
SAX-Ansatz
void CopySheetSAX(string path)
{
Console.WriteLine("Starting SAX method");
Stopwatch sw = new();
sw.Start();
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(path, true))
{
// Get the first sheet
WorksheetPart? worksheetPart = spreadsheetDocument.WorkbookPart?.WorksheetParts?.FirstOrDefault();
if (worksheetPart is not null)
{
WorksheetPart newWorksheetPart = spreadsheetDocument.WorkbookPart!.AddNewPart<WorksheetPart>();
using (OpenXmlReader reader = OpenXmlPartReader.Create(worksheetPart))
using (OpenXmlWriter writer = OpenXmlPartWriter.Create(newWorksheetPart))
{
// Write the XML declaration with the version "1.0".
writer.WriteStartDocument();
// Read the elements from the original worksheet part
while (reader.Read())
{
// If the ElementType is CellValue it's necessary to explicitly add the inner text of the element
// or the CellValue element will be empty
if (reader.ElementType == typeof(CellValue))
{
if (reader.IsStartElement)
{
writer.WriteStartElement(reader);
writer.WriteString(reader.GetText());
}
else if (reader.IsEndElement)
{
writer.WriteEndElement();
}
}
// For other elements write the start and end elements
else
{
if (reader.IsStartElement)
{
writer.WriteStartElement(reader);
}
else if (reader.IsEndElement)
{
writer.WriteEndElement();
}
}
}
}
Sheets? sheets = spreadsheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>();
if (sheets is null)
{
spreadsheetDocument.WorkbookPart.Workbook.AddChild(new Sheets());
}
string id = spreadsheetDocument.WorkbookPart.GetIdOfPart(newWorksheetPart);
uint newSheetId = (uint)(sheets!.ChildElements.Count + 1);
sheets.AppendChild(new Sheet() { Name = "My New Sheet", SheetId = newSheetId, Id = id });
sw.Stop();
Console.WriteLine($"SAX method took {sw.Elapsed.TotalSeconds} seconds");
}
}
}