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
具有所需的顺序:
以下是崩溃的原因:
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" };
该方法始终将新的子元素插入到正确的位置,以确保生成的文档有效。