[英]OpenXML SDK 2.5 unreadable content
我正在研究已經擁有所有公式和格式的現有Excel文件,我將數據添加到帶有表格的工作表中,然后當我在Excel中打開該文件時出現錯誤
“Excel已完成文件級驗證和修復。此工作簿的某些部分可能已被修復或丟棄。已移除的記錄:來自/xl/worksheets/sheet6.xml部分的單元信息”
然后我打開一個具有相同內容的手動創建的文件,它工作得很好。 我還發現了Open XML 2.5 Productivity Tool,當我對生成的文件運行驗證時,它表示沒有發現任何問題。
當我在兩個文件上運行比較時,我看到Generated文件看起來像這樣。
<x:c r="B462" t="inlineStr">
<x:is>
<x:t>1150828</x:t>
</x:is>
</x:c>
雖然手動創建的文件具有看起來像這樣的單元格。
<c s="80" r="B462">
<v>
1150828
</v>
</c>
顯然這里存在差異,但我不知道如何糾正它,也不知道這種差異是否是錯誤的實際原因。 但看到其他一切似乎看起來一樣,我不知道它還能是什么。
哦,還有更多的事情,這個文件不起作用,但我能夠使用另一個不包含表的文件,當我合並一個表時問題發生所以我至少知道那么多。
此外,如果您打算建議我使用ClosedXML,請不要。 我已經使用過它,因為我無法弄清楚為什么我已經轉移到OpenXML SDk,它往往會隨意放棄格式化
這是一些C#代碼
dt.Load(reader);
RowCount = dt.Rows.Count;
ColumnCount = dt.Columns.Count;
workbookPart = spreadDoc.WorkbookPart;
SheetDimension sheetDimension = new SheetDimension() { Reference = "A1:" + ColumnLetters[ColumnCount - 1] + (RowCount + 1) };
worksheetPart = Program.GetWorksheetPart(workbookPart, reportStep.ExcelSheetName);
worksheetPart.Worksheet.SheetDimension = sheetDimension;
SheetData sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();
string relId = workbookPart.Workbook.Descendants<Sheet>().First(s => reportStep.ExcelSheetName.Equals(s.Name)).Id;
if (reportStep.ExcelTableExists)
{
TableDefinitionPart tableDef = null;
int looper = 0;
foreach (WorksheetPart wsp in spreadDoc.WorkbookPart.WorksheetParts)
{
if (wsp.TableDefinitionParts.Where(tbl => tbl.Table.DisplayName.Value.Equals(reportStep.ExcelTableName)).Count() == 1)
{
tableDef = spreadDoc.WorkbookPart.WorksheetParts.ElementAt(looper).TableDefinitionParts.Where(tbl => tbl.Table.DisplayName.Value.Equals(reportStep.ExcelTableName)).FirstOrDefault();
tableDef.Table.Reference.Value = "A1:" + (ColumnLetters[ColumnCount - 1] + (RowCount +1) ).ToString();
tableDef.Table.AutoFilter.Reference.Value = "A1:" + (ColumnLetters[ColumnCount - 1] + (RowCount +1)).ToString();
// tabledefinitionPart = Program.GetTablePart(wsp, reportStep.ExcelTableName, ColumnCount, RowCount);
}
looper++;
}
}
sheetData = Chef.Program.ExportDataTable(dt, sheetData);
Sheet sheet = workbookPart.Workbook.Descendants<Sheet>().FirstOrDefault(s => s.Name == reportStep.ExcelSheetName);
public static TableDefinitionPart GetTablePart(WorksheetPart worksheet, string tablename, int columnCount, int rowCount)
{
uint CellRange = (uint)(columnCount);
TableColumns tableColumns1 = new TableColumns() { Count = (UInt32Value)(CellRange) };
var tableDefPart = worksheet.TableDefinitionParts.Where(tbl => tbl.Table.DisplayName.Value.Equals(tablename)).FirstOrDefault();
//worksheet.WorksheetPart.TableDefinitionParts.AddNewPart<TableDefinitionPart>(tablename);
var table = new Table() { HeaderRowCount = (uint)columnCount, Name = tablename, DisplayName = tablename, Reference = "A1:" + ColumnLetters[columnCount -1] + (rowCount + 1), TotalsRowShown = false };
TableStyleInfo tableStyleInfo1 = new TableStyleInfo()
{
Name = "TableStyleMedium2",
ShowFirstColumn = false,
ShowLastColumn = false,
ShowRowStripes = true,
ShowColumnStripes = false
};
table.Append(tableStyleInfo1);
// table.Append(tableColumns1);
tableDefPart.Table = table;
return tableDefPart;
}
編輯部分在要求的其他方法中添加更新於2015年9月5日更新
我刪除了添加標頭值的代碼,因為它們已經是excel文件基本模板的一部分。 還刪除了指定單元格數據類型以保留模板已經設置為單元格數據類型的內容。
public static SheetData ExportDataTable2(System.Data.DataTable exportData, SheetData sheetData)
{
//loop through each data row
DataRow contentRow;
int startRow = 2;
for (int i = 0; i < exportData.Rows.Count; i++)
{
contentRow = exportData.Rows[i];
sheetData.AppendChild(createContentRow(contentRow, i + startRow));
}
return sheetData;
}
private static Cell createTextCell(int columnIndex, int rowIndex, object cellValue)
{
Cell cell = new Cell();
// cell.DataType = CellValues.Number;
cell.CellReference = getColumnName(columnIndex) + rowIndex;
cell.CellValue = new CellValue(cellValue.ToString());
return cell;
}
private static Row createContentRow(DataRow dataRow, int rowIndex)
{
Row row = new Row
{
RowIndex = (UInt32)rowIndex
};
for (int i = 0; i < dataRow.Table.Columns.Count; i++)
{
Cell dataCell = createTextCell(i + 1, rowIndex, dataRow[i]);
// dataCell.DataType = CellValues.SharedString;
row.AppendChild(dataCell);
}
return row;
}
好吧,您似乎使用了以下示例OpenXML SDK 2.0:將DataTable導出為Excel作為代碼的基礎。 以下是創建單元格的原始代碼:
private Cell createTextCell(int columnIndex, int rowIndex, object cellValue)
{
Cell cell = new Cell();
cell.DataType = CellValues.InlineString;
cell.CellReference = getColumnName(columnIndex) + rowIndex;
InlineString inlineString = new InlineString();
Text t = new Text();
t.Text = cellValue.ToString();
inlineString.AppendChild(t);
cell.AppendChild(inlineString);
return cell;
}
您的原始代碼完全相同,但以下行除外:
cell.DataType = CellValues.String;
看到不同?
然后你把它改成了:
private static Cell createTextCell(int columnIndex, int rowIndex, object cellValue)
{
Cell cell = new Cell();
// cell.DataType = CellValues.Number;
cell.CellReference = getColumnName(columnIndex) + rowIndex;
cell.CellValue = new CellValue(cellValue.ToString());
return cell;
}
好的,問題是你沒有正確設置cell.DataType
。 它需要與單元格內容同步,否則您將從Excel中獲得此類錯誤。 在前一種情況下,您將內容設置為inline string
,但將數據類型設置為String
。 在后面 - 數據類型為Number
(你注釋了行沒有關系 - Number
是單元格的默認數據類型)但是內容並不總是一個數字(相同的函數用於列headears - 之后所有,它被稱為創建文本單元格)。
要解決此問題,請使用示例中的原始代碼或此代碼:
private static Cell createTextCell(int columnIndex, int rowIndex, object cellValue)
{
Cell cell = new Cell();
cell.DataType = CellValues.String;
cell.CellReference = getColumnName(columnIndex) + rowIndex;
cell.CellValue = new CellValue(cellValue.ToString());
return cell;
}
最后,如果您需要存儲共享字符串,數字,日期等,請閱讀文檔並設置相應的屬性。 我認為OpenXml API不是很直觀,但這就是我們所擁有的。
編輯:根據您的意見,似乎您的真正問題不完全是問題。 以下是使用不同數據類型列導出DataTable
高性能示例:
public static class ExcelExporter
{
public static void ExportDataTable(DataTable table, SheetData data)
{
var cellFactory = new CellFactory[table.Columns.Count];
for (int i = 0; i < table.Columns.Count; i++)
cellFactory[i] = GetCellFactory(table.Columns[i].DataType);
int rowIndex = 0;
data.AppendChild(CreateHeaderRow(rowIndex++, table));
for (int i = 0; i < table.Rows.Count; i++)
data.AppendChild(CreateContentRow(rowIndex++, table.Rows[i], cellFactory));
}
private static Row CreateHeaderRow(int rowIndex, DataTable table)
{
var row = CreateRow(rowIndex);
for (int i = 0; i < table.Columns.Count; i++)
{
var cell = CreateTextCell(i, rowIndex, table.Columns[i].ColumnName);
row.AppendChild(cell);
}
return row;
}
private static Row CreateContentRow(int rowIndex, DataRow dataRow, CellFactory[] cellFactory)
{
var row = CreateRow(rowIndex);
for (int i = 0; i < dataRow.Table.Columns.Count; i++)
{
var cell = cellFactory[i](i, rowIndex, dataRow[i]);
row.AppendChild(cell);
}
return row;
}
private static Row CreateRow(int index) { return new Row { RowIndex = (uint)index + 1 }; }
private delegate Cell CellFactory(int columnIndex, int rowIndex, object cellValue);
private static CellFactory GetCellFactory(Type dataType)
{
CellFactory factory;
return CellFactoryMap.TryGetValue(dataType, out factory) ? factory : TextCellFactory;
}
private static readonly CellFactory TextCellFactory = CreateTextCell;
private static readonly CellFactory DateCellFactory = CreateDateCell;
private static readonly CellFactory NumericCellFactory = CreateNumericCell;
private static readonly CellFactory BooleanCellFactory = CreateBooleanCell;
private static readonly Dictionary<Type, CellFactory> CellFactoryMap = new Dictionary<Type, CellFactory>
{
{ typeof(bool), BooleanCellFactory },
{ typeof(DateTime), DateCellFactory },
{ typeof(byte), NumericCellFactory },
{ typeof(sbyte), NumericCellFactory },
{ typeof(short), NumericCellFactory },
{ typeof(ushort), NumericCellFactory },
{ typeof(int), NumericCellFactory },
{ typeof(uint), NumericCellFactory },
{ typeof(long), NumericCellFactory },
{ typeof(ulong), NumericCellFactory },
{ typeof(float), NumericCellFactory },
{ typeof(double), NumericCellFactory },
{ typeof(decimal), NumericCellFactory },
};
private static Cell CreateTextCell(int columnIndex, int rowIndex, object cellValue)
{
return CreateCell(CellValues.String, columnIndex, rowIndex, ToExcelValue(cellValue));
}
private static Cell CreateDateCell(int columnIndex, int rowIndex, object cellValue)
{
// NOTE: CellValues.Date is not supported in older Excel version.
// In all Excel versions dates can be stored with CellValues.Number and a format style.
// Since I have no styles, will export them just as text
//var cell = CreateCell(CellValues.Number, columnIndex, rowIndex, ToExcelDate(cellValue));
//cell.StyleIndex = ...;
//return cell;
return CreateCell(CellValues.String, columnIndex, rowIndex,
cellValue != null && cellValue != DBNull.Value ? ((DateTime)cellValue).ToShortDateString() : null);
}
private static Cell CreateNumericCell(int columnIndex, int rowIndex, object cellValue)
{
return CreateCell(CellValues.Number, columnIndex, rowIndex, ToExcelValue(cellValue));
}
private static Cell CreateBooleanCell(int columnIndex, int rowIndex, object cellValue)
{
// NOTE: CellValues.Boolean is not supported in older Excel version
//return CreateCell(CellValues.Boolean, columnIndex, rowIndex, ToExcelValue(cellValue));
return CreateCell(CellValues.String, columnIndex, rowIndex, ToExcelValue(cellValue));
}
private static Cell CreateCell(CellValues dataType, int columnIndex, int rowIndex, string cellValue)
{
var cell = new Cell();
if (dataType != CellValues.Number) cell.DataType = dataType;
cell.CellReference = GetColumnName(columnIndex) + (rowIndex + 1);
cell.CellValue = new CellValue(cellValue ?? string.Empty);
return cell;
}
private static string ToExcelValue(object value)
{
if (value == null || value == DBNull.Value) return null;
return Convert.ToString(value, CultureInfo.InvariantCulture);
}
private static DateTime ExcelBaseDate = new DateTime(1900, 1, 1);
private static string ToExcelDate(object value)
{
const int days29Feb1900 = 59;
if (value == null || value == DBNull.Value) return null;
var date = ((DateTime)value).Date;
var days = (date - ExcelBaseDate).Days + 1;
if (days >= days29Feb1900) days++;
return days.ToString(CultureInfo.InvariantCulture);
}
private static string GetColumnName(int index) { return ColumnNameTable[index]; }
private static readonly string[] ColumnNameTable = BuildColumnNameTable();
private static string[] BuildColumnNameTable()
{
var table = new string[16384];
var sb = new StringBuilder();
for (int i = 0; i < table.Length; i++)
table[i] = sb.BuildColumnName(i);
return table;
}
private static string BuildColumnName(this StringBuilder sb, int index)
{
const int startLetter = 'A';
const int letterCount = 'Z' - startLetter + 1;
sb.Clear();
while (true)
{
var letter = (char)(startLetter + (index % letterCount));
sb.Insert(0, letter);
if (index < letterCount) break;
index = (index / letterCount) - 1;
}
return sb.ToString();
}
}
關鍵是,不是在處理過程中檢查每個值的類型,而是在開始時為每列准備基於其數據類型的不同創建單元格方法。
關於使用OpenXml Sdk的無效文件,我也遇到了問題。 看看OpenXml Power Tools ; 他們已經解決了我所有的問題:)另外你應該切換到OpenXml Sdk 2.6以避免System.IO.Packaging
問題。 我希望這有幫助!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.