8

openxml を使用して、自動化された Excel ファイルを生成しようとしています。私が直面している問題の 1 つは、オブジェクト モデルを 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 ファイルを開くことができるはずなので、必要に応じてワークシートの新しい子要素を作成して追加します。しかし、これにより、これらの要素が追加されるはずの順序が崩れる可能性があります。

ありがとう。

4

5 に答える 5

9

Standard ECMA-376 Office Open XML File FormatsによるとCT_Worksheet、必要なシーケンスがあります。

CT_Worksheet スキーマ ダイアグラム

以下がクラッシュする理由:

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

あなたがdrawing 前に mergeCells持っているからです。mergeCells after を追加する限りdrawing、コードは正常に動作するはずです。

注: 完全な XSD は、ECMA-376 3rd edition Part 1 (.zip) -> OfficeOpenXML-XMLSchema-Strict -> sml.xsd にあります。

于 2012-08-31T19:32:20.703 に答える
1

ジョー・マシロッティがすでに説明したように、順序はスキーマで定義されています。

残念ながら、OpenXML ライブラリは、シリアライズされた XML 内の子要素の正しい順序を、基になる XML スキーマで必要とされるように保証しません。順序が正しくない場合、アプリケーションは XML を正常に解析できない場合があります。

これは、コードで使用している一般的なソリューションです。

private T GetOrCreateWorksheetChildCollection<T>(Spreadsheet.Worksheet worksheet) 
    where T : OpenXmlCompositeElement, new()
{
    T collection = worksheet.GetFirstChild<T>();
    if (collection == null)
    {
        collection = new T();
        if (!worksheet.HasChildren)
        {
            worksheet.AppendChild(collection);
        }
        else
        {
            // compute the positions of all child elements (existing + new collection)
            List<int> 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<string> _childElementNames = new List<string>() { 
    "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"
};

メソッドは常に正しい位置に新しい子要素を挿入し、結果のドキュメントが有効であることを保証します。

于 2016-08-04T06:06:39.693 に答える
1

私が行ったように、Google経由でここにたどり着いた場合、以下の関数は、子要素が挿入された後の順序付けの問題を解決します:

public static T ReorderChildren<T>(T element) where T : OpenXmlElement
{
  Dictionary<Type, int> childOrderHashTable = element.GetType()
                                                  .GetCustomAttributes()
                                                  .Where(x => x is ChildElementInfoAttribute)
                                                  .Select( (x, idx) => new KeyValuePair<Type, int>(((ChildElementInfoAttribute)x).ElementType, idx))
                                                  .ToDictionary(x => x.Key, x => x.Value);

  List<OpenXmlElement> reorderedChildren = element.ChildElements
                                                .OrderBy(x => childOrderHashTable[x.GetType()])
                                                .ToList();
  element.RemoveAllChildren();
  element.Append(reorderedChildren);
  return element;         
}

ライブラリで生成された型にはDocumentFormat.OpenXml、OOXML スキーマからのメタデータを反映するために使用できるカスタム属性があります。System.Reflectionこのソリューションはandに依存していますSystem.Linq(つまり、それほど高速ではありません) が、特定の型の子要素を正しく順序付けるために文字列のリストをハードコーディングする必要がなくなります。

プロパティの検証後にこの関数を使用ValidationErrorInfo.Nodeし、新しく作成された要素を参照によってクリーンアップします。そうすれば、このメソッドをドキュメント全体に再帰的に適用する必要がなくなります。

于 2021-08-02T20:42:55.777 に答える