OpenXml:工作表子元素更改排序导致损坏的文件

我正在尝试使用openxml来生成自动excel文件。 我面临的一个问题是使用excel的开放式xml对象模型来容纳我的对象模型。 我必须达到一个点,我意识到我为工作表附加子元素的顺序很重要。

例如:

workSheet.Append(sheetViews); workSheet.Append(columns); workSheet.Append(sheetData); workSheet.Append(mergeCells); workSheet.Append(drawing); 

上面的排序不会给出任何错误。

但以下内容:

 workSheet.Append(sheetViews); workSheet.Append(columns); workSheet.Append(sheetData); workSheet.Append(drawing); workSheet.Append(mergeCells); 

给出错误

所以这不允许我随时创建一个绘图对象并将其附加到工作表中。 这迫使我在使用它们之前创建这些元素。

任何人都可以告诉我,我是否正确理解了这个问题? 因为我相信我们应该能够打开任何excel文件,必要时为工作表创建一个新的子元素并附加它。 但现在这可能会破坏这些元素应该被追加的顺序。

谢谢。

根据标准ECMA-376 Office Open XML文件格式 , CT_Worksheet具有所需的顺序:

CT_Worksheet架构图

以下是崩溃的原因:

 workSheet.Append(sheetViews); workSheet.Append(columns); workSheet.Append(sheetData); workSheet.Append(drawing); workSheet.Append(mergeCells); 

是因为你 mergeCells 之前drawing 。 只要您 drawing 附加mergeCells ,您的代码应该可以正常工作。

注意:您可以在ECMA-376第3版第1部分(.zip) – > OfficeOpenXML-XMLSchema-Strict – > sml.xsd中找到完整的XSD。

我发现对于所有“Singleton”子节点,其中父对象具有已定义的属性(例如Worksheet.sheetViews),使用singleton属性并将新对象分配给它而不是使用“Append”这会导致类本身确保顺序是正确的。

 workSheet.Append(sheetViews); workSheet.Append(columns); workSheet.Append(sheetData); // bad idea(though it does work if the order is good) workSheet.Append(drawing); workSheet.Append(mergeCells); 

更正确的格式……

 workSheet.sheetViews=sheetViews; // order doesn't matter. workSheet.columns=columns; ... 

正如Joe Masilotti 已经解释的那样 ,订单是在架构中定义的。

遗憾的是,OpenXML库无法确保基本XML模式所要求的序列化XML中子元素的正确顺序。 如果订单不正确,应用程序可能无法成功解析XML。

这是我在代码中使用的通用解决方案:

 private T GetOrCreateWorksheetChildCollection(Spreadsheet.Worksheet worksheet) where T : OpenXmlCompositeElement, new() { T collection = worksheet.GetFirstChild(); if (collection == null) { collection = new T(); if (!worksheet.HasChildren) { worksheet.AppendChild(collection); } else { // compute the positions of all child elements (existing + new collection) List schemaPositions = worksheet.ChildElements .Select(e => _childElementNames.IndexOf(e.LocalName)).ToList(); int collectionSchemaPos = _childElementNames.IndexOf(collection.LocalName); schemaPositions.Add(collectionSchemaPos); schemaPositions = schemaPositions.OrderBy(i => i).ToList(); // now get the index where the position of the new child is int index = schemaPositions.IndexOf(collectionSchemaPos); // this is the index to insert the new element worksheet.InsertAt(collection, index); } } return collection; } // names and order of possible child elements according to the openXML schema private static readonly List _childElementNames = new List() { "sheetPr", "dimension", "sheetViews", "sheetFormatPr", "cols", "sheetData", "sheetCalcPr", "sheetProtection", "protectedRanges", "scenarios", "autoFilter", "sortState", "dataConsolidate", "customSheetViews", "mergeCells", "phoneticPr", "conditionalFormatting", "dataValidations", "hyperlinks", "printOptions", "pageMargins", "pageSetup", "headerFooter", "rowBreaks", "colBreaks", "customProperties", "cellWatches", "ignoredErrors", "smartTags", "drawing", "drawingHF", "picture", "oleObjects", "controls", "webPublishItems", "tableParts", "extLst" }; 

该方法始终将新的子元素插入到正确的位置,以确保生成的文档有效。