Excel 保存文档时如何在编辑后保留 OpenXml 单元格属性?
How do you preserve OpenXml cell attributes after an edit when Excel saves the document?
我想导出包含数据的 excel 文件,以便我的用户可以:
- 下载 Excel 文件(从我的程序导出)
- 编辑文件中的数据
- 保存
- 上传 Excel 文件(将其重新导入我的程序)
基本上我会给他们提供离线文件的体验,如果他们没有任何互联网访问权限(因为我们的是网络应用程序),他们可以编辑该文件
创建Excel 文件时使用OpenXml SDK I use the OpenXmlElement.SetAttribute 方法向工作表的列、行和单元格添加属性。使用我添加的属性,以便在重新导入时我可以将编辑后的数据与它应该存储的位置相匹配。
我导出的属性是:
- 数据库 ID
- 原始值(导出时的数据库值,以便在导入时轻松同步)
- 最后修改导出数据的日期
单元格的导出例程如下所示:
var cell = new Cell {
CellReference = string.Format("{0}{1}", Column.Reference, Row.Index),
DataType = this.CellDataType
};
foreach (var keyValuePair in this.AttributeDictionary) {
cell.SetAttribute(new OpenXmlAttribute {
LocalName = keyValuePair.Key,
Value = keyValuePair.Value.ToString()
});
}
这个导出工作正常。在 OpenXml Productivity Tool 中检查导出的文件时,我可以看到属性已正确添加。在 Excel 中编辑后保存文件时,不会保留属性。有没有办法告诉 Excel 保留属性,或者是否有其他程序最适合用于保留我需要的数据以便轻松重新导入数据?
附带问题:
如果 Excel 不保留它们的属性是什么?
我不认为你可以强制 Excel 往返未知 属性 但你可以添加扩展 元素 使用ExtensionLists
和 Extensions
。 Excel 将往返这些元素并设计(据我所知)用于存储特定于应用程序的数据,就像您想要的那样。
似乎没有太多我能找到的文档,但 ECMA-376 spec 的第 3 部分提到了扩展。
下面的代码将在单元格 A1 中创建一个具有值的 sheet 和一个具有一个 Extension
的 ExtensionList
作为该单元格的子单元:
public static void CreateSpreadsheetWorkbook(string filepath)
{
if (File.Exists(filepath))
File.Delete(filepath);
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filepath, SpreadsheetDocumentType.Workbook))
{
// Add a WorkbookPart to the document.
WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
// Add a WorksheetPart to the WorkbookPart.
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
SheetData sheetData = new SheetData();
worksheetPart.Worksheet = new Worksheet(sheetData);
// Add Sheets to the Workbook.
Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());
// Append a new worksheet and associate it with the workbook.
Sheet sheet = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.
GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "Sheet1"
};
sheets.Append(sheet);
Row row = new Row()
{
RowIndex = 1U
};
Cell cell = new Cell()
{
CellReference = "A1",
CellValue = new CellValue("A Test"),
DataType = CellValues.String
};
ExtensionList extensions = new ExtensionList();
Extension extension = new Extension()
{
Uri = "Testing1234"
};
extensions.AppendChild(extension);
extension.AddNamespaceDeclaration("ns", "http://tempuri/someUrl");
cell.AppendChild(extensions);
row.Append(cell);
sheetData.Append(row);
workbookpart.Workbook.Save();
// Close the document.
spreadsheetDocument.Close();
}
}
即使文件已经通过 Excel.
来回读取,以下内容也会再次读回该值
public static void ReadSheet(string filename, string sheetName)
{
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(filename, false))
{
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
//get the correct sheet
Sheet sheet = workbookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == sheetName).First();
if (sheet != null)
{
WorksheetPart worksheetPart = workbookPart.GetPartById(sheet.Id) as WorksheetPart;
foreach (Cell cell in worksheetPart.Worksheet.Descendants<Cell>())
{
ExtensionList extensions = cell.GetFirstChild<ExtensionList>();
if (extensions != null)
{
Extension extension = extensions.GetFirstChild<Extension>();
if (extension != null)
{
Console.WriteLine("Cell {0} has value {1}", cell.CellReference, extension.Uri);
}
}
}
}
}
}
其中的输出是
Cell A1 has value Testing1234
关于你的附带问题:
What are the attributes for if Excel does not preserve them?
我不太确定 - 我唯一一次使用 OpenXmlAttribute
class 是在我使用 SAX 方法编写文档时。在那种情况下,您需要将属性与元素一起显式编写。例如:
List<OpenXmlAttribute> oxa = new List<OpenXmlAttribute>();
//cell reference attribute
oxa.Add(new OpenXmlAttribute("r", "", "A1"));
//cell type attribute
oxa.Add(new OpenXmlAttribute("t", "", "str"));
//write the start element of a cell with the above attributes
oxw.WriteStartElement(new Cell(), oxa);
//write a value to the cell
oxw.WriteElement(new CellValue("Test"));
//write the end element
oxw.WriteEndElement();
我的 answer here 有一个使用 SAX 方法的完整示例。
我想导出包含数据的 excel 文件,以便我的用户可以:
- 下载 Excel 文件(从我的程序导出)
- 编辑文件中的数据
- 保存
- 上传 Excel 文件(将其重新导入我的程序)
基本上我会给他们提供离线文件的体验,如果他们没有任何互联网访问权限(因为我们的是网络应用程序),他们可以编辑该文件
创建Excel 文件时使用OpenXml SDK I use the OpenXmlElement.SetAttribute 方法向工作表的列、行和单元格添加属性。使用我添加的属性,以便在重新导入时我可以将编辑后的数据与它应该存储的位置相匹配。
我导出的属性是:
- 数据库 ID
- 原始值(导出时的数据库值,以便在导入时轻松同步)
- 最后修改导出数据的日期
单元格的导出例程如下所示:
var cell = new Cell {
CellReference = string.Format("{0}{1}", Column.Reference, Row.Index),
DataType = this.CellDataType
};
foreach (var keyValuePair in this.AttributeDictionary) {
cell.SetAttribute(new OpenXmlAttribute {
LocalName = keyValuePair.Key,
Value = keyValuePair.Value.ToString()
});
}
这个导出工作正常。在 OpenXml Productivity Tool 中检查导出的文件时,我可以看到属性已正确添加。在 Excel 中编辑后保存文件时,不会保留属性。有没有办法告诉 Excel 保留属性,或者是否有其他程序最适合用于保留我需要的数据以便轻松重新导入数据?
附带问题:
如果 Excel 不保留它们的属性是什么?
我不认为你可以强制 Excel 往返未知 属性 但你可以添加扩展 元素 使用ExtensionLists
和 Extensions
。 Excel 将往返这些元素并设计(据我所知)用于存储特定于应用程序的数据,就像您想要的那样。
似乎没有太多我能找到的文档,但 ECMA-376 spec 的第 3 部分提到了扩展。
下面的代码将在单元格 A1 中创建一个具有值的 sheet 和一个具有一个 Extension
的 ExtensionList
作为该单元格的子单元:
public static void CreateSpreadsheetWorkbook(string filepath)
{
if (File.Exists(filepath))
File.Delete(filepath);
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filepath, SpreadsheetDocumentType.Workbook))
{
// Add a WorkbookPart to the document.
WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
// Add a WorksheetPart to the WorkbookPart.
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
SheetData sheetData = new SheetData();
worksheetPart.Worksheet = new Worksheet(sheetData);
// Add Sheets to the Workbook.
Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());
// Append a new worksheet and associate it with the workbook.
Sheet sheet = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.
GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "Sheet1"
};
sheets.Append(sheet);
Row row = new Row()
{
RowIndex = 1U
};
Cell cell = new Cell()
{
CellReference = "A1",
CellValue = new CellValue("A Test"),
DataType = CellValues.String
};
ExtensionList extensions = new ExtensionList();
Extension extension = new Extension()
{
Uri = "Testing1234"
};
extensions.AppendChild(extension);
extension.AddNamespaceDeclaration("ns", "http://tempuri/someUrl");
cell.AppendChild(extensions);
row.Append(cell);
sheetData.Append(row);
workbookpart.Workbook.Save();
// Close the document.
spreadsheetDocument.Close();
}
}
即使文件已经通过 Excel.
来回读取,以下内容也会再次读回该值public static void ReadSheet(string filename, string sheetName)
{
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(filename, false))
{
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
//get the correct sheet
Sheet sheet = workbookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == sheetName).First();
if (sheet != null)
{
WorksheetPart worksheetPart = workbookPart.GetPartById(sheet.Id) as WorksheetPart;
foreach (Cell cell in worksheetPart.Worksheet.Descendants<Cell>())
{
ExtensionList extensions = cell.GetFirstChild<ExtensionList>();
if (extensions != null)
{
Extension extension = extensions.GetFirstChild<Extension>();
if (extension != null)
{
Console.WriteLine("Cell {0} has value {1}", cell.CellReference, extension.Uri);
}
}
}
}
}
}
其中的输出是
Cell A1 has value Testing1234
关于你的附带问题:
What are the attributes for if Excel does not preserve them?
我不太确定 - 我唯一一次使用 OpenXmlAttribute
class 是在我使用 SAX 方法编写文档时。在那种情况下,您需要将属性与元素一起显式编写。例如:
List<OpenXmlAttribute> oxa = new List<OpenXmlAttribute>();
//cell reference attribute
oxa.Add(new OpenXmlAttribute("r", "", "A1"));
//cell type attribute
oxa.Add(new OpenXmlAttribute("t", "", "str"));
//write the start element of a cell with the above attributes
oxw.WriteStartElement(new Cell(), oxa);
//write a value to the cell
oxw.WriteElement(new CellValue("Test"));
//write the end element
oxw.WriteEndElement();
我的 answer here 有一个使用 SAX 方法的完整示例。