2012-08-02 17 views
6

Estoy intentando usar openxml para producir archivos automatizados de Excel. Un problema que estoy enfrentando es acomodar mi modelo de objetos con el modelo de objetos xml abierto para Excel. Tengo que llegar al punto en el que me doy cuenta de que el orden en el que anexo los elementos secundarios para una hoja de trabajo es importante.OpenXml: Elementos secundarios de la hoja de trabajo cambian al ordenar los resultados en un archivo corrupto

Por ejemplo:

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

el ordenamiento anterior doesnot dar cualquier error.

Pero el siguiente:

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

da un error

Así que esto no me permite crear un objeto de dibujo cada vez que quiero y lo añaden a la hoja de trabajo. Lo cual me obliga a crear estos elementos antes de usarlos.

¿Alguien puede decirme si he entendido el problema correctamente? Como creo que deberíamos poder abrir cualquier archivo de Excel, cree un nuevo elemento hijo para una hoja de trabajo si es necesario y añádalo. Pero ahora esto puede romper el orden en que se supone que estos elementos se anexan.

Gracias.

Respuesta

6

De acuerdo con la Standard ECMA-376 Office Open XML File Formats, CT_Worksheet tiene una secuencia requerida:

CT_Worksheet Schema Diagram

La razón es la siguiente estrellarse:

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

es porque tiene drawingantesmergeCells. Siempre que agregue su mergeCellsdespués dedrawing, su código debería funcionar bien.

Nota: Puede encontrar el XSD completo en ECMA-376 3rd edition Part 1 (.zip) -> OfficeOpenXML-XMLSchema-Strict -> sml.xsd.

1

Encontré que para todos los hijos "Singleton" donde los objetos principales tienen una propiedad definida (como Worksheet.sheetViews) use la propiedad singleton y asigne el nuevo objeto a eso en lugar de usar "Append" Esto causa la clase misma para asegurar que el orden sea correcto

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

formato más correcta ...

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

Como Joe Masilotti already explained, el orden se define en el esquema.

Desafortunadamente, la biblioteca OpenXML no garantiza el orden correcto de los elementos secundarios en el XML serializado como lo requiere el esquema XML subyacente. Es posible que las aplicaciones no puedan analizar correctamente el XML si el orden no es correcto.

Aquí es una solución genérica que estoy usando en mi código:

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

El método siempre inserta el nuevo elemento secundario en la posición correcta, asegurando que el documento resultante es válida.

Cuestiones relacionadas