当Excel保存文档时,如何在编辑后保留OpenXml单元格属性

本文关键字:编辑 保留 OpenXml 属性 单元格 保存 Excel 文档 | 更新日期: 2023-09-27 18:00:45

我想导出一个包含数据的excel文件,以便我的用户可以:

  • 下载Excel文件(从我的程序导出)
  • 编辑文件中的数据
  • 保存它
  • 上传Excel文件(将其重新导入我的程序)

基本上,我会给他们一个离线文件的体验,如果他们没有任何互联网接入(因为我们的是一个网络应用程序),他们可以编辑

当使用OpenXml SDK创建Excel文件时,我使用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保存文档时,如何在编辑后保留OpenXml单元格属性

我不认为您可以强制Excel往返未知的属性,但您可以使用ExtensionListsExtensions添加扩展元素。Excel将往返这些元素,并被设计(据我所知)用于存储特定于应用程序的数据,就像您所追求的那样。

我似乎找不到太多的文档,但ECMA-376规范的第3部分提到了扩展。

以下代码将创建一个单元格A1中有值的工作表和一个单元格中有一个ExtensionExtensionList,作为该单元格的子单元格:

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);
                    }
                }
            }
        }
    }
}

输出为

单元格A1具有Testing1234 值

至于你的问题:

如果Excel不保留这些属性,它们的作用是什么?

我不太确定——我唯一一次使用OpenXmlAttribute类是在使用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();

我在这里的回答有一个使用SAX方法的完整示例。