ExcelOperations Library - ExcelToDataSet, DataSetToExcel
This function library does two things mainly.
- Takes an Excel binary (
.xlsx
) and exports it to a DataSet. - Takes a DataSet and exports it to an Excel binary. (
.xlsx
)
It also includes a helper function that will open an excel file on the server, and run it through the ExcelToDataSet function for you.
ExcelToDataSet will export all sheets in an excel file (.xlsx
) to a DataSet. Each sheet get’s it’s own table.
DataSetToExcel will export all tables in a DataSet to an excel file (.xlsx
). Each table get’s its own sheet.
ReadExcelFileReturnDataSet will read an excel file and run it through ExcelToDataSet
and return a DataSet.
Library → ExcelOperations.efxj (17.5 KB)
Here are the guts →
References →
- DocumentFormat.OpenXml.dll
- Newtonsoft.Json.dll
- System.IO.Packaging.dll
Function → ExcelToDataSet
Signature →
Name | Type |
---|---|
b64Data | System.String |
hasHeaderRows | System.Boolean |
Usings →
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using Newtonsoft.Json;
Code →
Func<SpreadsheetDocument, List<string>> GetWorksheetNames = (doc) =>
{
return doc.WorkbookPart.Workbook.Sheets
.Elements<Sheet>()
.Select(s => s.Name?.Value)
.Where(name => name != null)
.ToList();
};
Func<Cell, SharedStringTable, object> GetCellValue = (cell, sharedStrings) =>
{
if (cell == null || cell.CellValue == null)
return DBNull.Value;
string value = cell.CellValue.Text;
if (cell.DataType != null && cell.DataType.HasValue)
{
CellValues dataType = cell.DataType.Value;
if (dataType == CellValues.SharedString)
{
int index;
if (int.TryParse(value, out index) && sharedStrings != null)
return sharedStrings.ElementAt(index).InnerText;
return value;
}
else if (dataType == CellValues.Boolean)
{
return value == "1" ? true : false;
}
else
{
return value; // inline strings etc.
}
}
else
{
// Try parse as int first
int intVal;
if (int.TryParse(value, out intVal))
return intVal;
// Then try date (Excel stores as OADate)
double oaDate;
if (double.TryParse(value, out oaDate) && oaDate > 0 && oaDate < 60000)
{
try
{
DateTime dt = DateTime.FromOADate(oaDate);
if (dt.Year > 1900 && dt.Year < 2100)
return dt;
}
catch { }
}
// Fallback to double
double dbl;
if (double.TryParse(value, out dbl))
return dbl;
return value;
}
};
Func<SpreadsheetDocument, string, bool, DataTable> ParseExcelFile = (xlDoc, sheet, hasHeader) =>
{
WorkbookPart workbookPart = xlDoc.WorkbookPart;
Sheet sheetMeta = workbookPart.Workbook.Sheets.Elements<Sheet>().FirstOrDefault(x => x.Name == sheet);
if (sheetMeta == null) throw new Exception("Sheet not found");
WorksheetPart worksheetPart = (WorksheetPart)workbookPart.GetPartById(sheetMeta.Id);
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
SharedStringTable sharedStringTable = workbookPart.SharedStringTablePart != null
? workbookPart.SharedStringTablePart.SharedStringTable
: null;
List<Row> rows = sheetData.Elements<Row>().ToList();
if (rows.Count == 0) return null;
DataTable table = new DataTable(sheet.Replace(" ", ""));
int startRowIndex = 0;
// Use typeof(object) to allow mixed data types
if (hasHeader)
{
Row headerRow = rows[0];
foreach (Cell cell in headerRow.Elements<Cell>())
{
string columnName = GetCellValue(cell, sharedStringTable)?.ToString();
table.Columns.Add(string.IsNullOrEmpty(columnName)
? "Column" + (table.Columns.Count + 1)
: columnName, typeof(object));
}
startRowIndex = 1;
}
else
{
int columnCount = rows.Max(r => r.Elements<Cell>().Count());
for (int i = 0; i < columnCount; i++)
{
table.Columns.Add("Column" + (i + 1), typeof(object));
}
}
for (int i = startRowIndex; i < rows.Count; i++)
{
Row row = rows[i];
List<Cell> cells = row.Elements<Cell>().ToList();
DataRow dataRow = table.NewRow();
for (int j = 0; j < table.Columns.Count; j++)
{
if (j < cells.Count)
dataRow[j] = GetCellValue(cells[j], sharedStringTable);
else
dataRow[j] = DBNull.Value;
}
table.Rows.Add(dataRow);
}
return table;
};
#if !DEBUG
Func<string, bool, DataSet> BuildDataSetFromSpreadSheet = (b64, hr) =>
{
using (var memoryStream = new System.IO.MemoryStream(Convert.FromBase64String(b64)))
{
using (var xlDoc = SpreadsheetDocument.Open(memoryStream, false))
{
var listSheets = GetWorksheetNames(xlDoc);
DataSet tempDS = new DataSet();
listSheets.ForEach(x =>
{
tempDS.Tables.Add(ParseExcelFile(xlDoc, x, hr));
});
return tempDS;
}
}
};
#else
Func<string, bool, DataSet> BuildDataSetFromSpreadSheet = (b64, hr) => null;
#endif
//Execution begins here:
var ListErrors = new List<Exception>();
try
{
#if !DEBUG
Success = true;
#else
throw new BLException("Library was left in DEBUG Mode.");
#endif
ds = BuildDataSetFromSpreadSheet(b64Data, hasHeaderRows); //hasHeaderRows is global for all sheets
}
catch (Exception ex)
{
Success = false;
ListErrors.Add(ex);
}
finally
{
ListErrorsJson = JsonConvert.SerializeObject(ListErrors, Formatting.Indented);
}
Function → DataSetToExcel
Signature →
Name | Type |
---|---|
ds | System.Data.DataSet |
writeHeaderRows | System.Boolean |
Usings →
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using Newtonsoft.Json;
Code →
Func<object, Cell> CreateCellFromValue = (value) =>
{
var cell = new Cell();
if (value == null || value == DBNull.Value)
{
cell.DataType = CellValues.String;
cell.CellValue = new CellValue("");
}
else if (value is bool)
{
cell.DataType = CellValues.Boolean;
cell.CellValue = new CellValue((bool)value ? "1" : "0");
}
else if (value is int || value is long || value is float || value is double || value is decimal)
{
// Leave DataType null to let Excel treat it as number
cell.CellValue = new CellValue(Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture));
}
else
{
cell.DataType = CellValues.String;
var str = value.ToString();
if (str.StartsWith("'")) // Remove Excel-style forced text apostrophe
str = str.Substring(1);
cell.CellValue = new CellValue(str);
}
return cell;
};
//Put library in debug mode while coding for the error to go away, take it off before saving
#if !DEBUG
Func<DataSet, byte[]> ExportToExcelAsBytes = (dataSet) =>
{
using (var stream = new System.IO.MemoryStream())
{
//Create Excel document
using (SpreadsheetDocument doc = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook, true))
{
WorkbookPart workbookPart = doc.AddWorkbookPart();
workbookPart.Workbook = new Workbook();
Sheets sheets = workbookPart.Workbook.AppendChild(new Sheets());
uint sheetId = 1;
//Loop through each table in the dataset
foreach (DataTable table in dataSet.Tables)
{
WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
SheetData sheetData = new SheetData();
worksheetPart.Worksheet = new Worksheet(sheetData);
if(writeHeaderRows)
{
// Header row
Row headerRow = new Row();
foreach (DataColumn col in table.Columns)
{
headerRow.AppendChild(CreateCellFromValue(col.ColumnName));
}
sheetData.AppendChild(headerRow);
}
// Data rows
foreach (DataRow row in table.Rows)
{
Row newRow = new Row();
foreach (var item in row.ItemArray)
{
newRow.AppendChild(CreateCellFromValue(item));
}
sheetData.AppendChild(newRow);
}
string relId = workbookPart.GetIdOfPart(worksheetPart);
sheets.Append(new Sheet
{
Id = relId,
SheetId = sheetId++,
Name = table.TableName
});
}
workbookPart.Workbook.Save();
}
return stream.ToArray(); //Return byte array
}
};
#else
Func<DataSet, byte[]> ExportToExcelAsBytes = (dataSet) => null;
#endif
var ListErrors = new List<Exception>();
try
{
#if !DEBUG
Success = true;
#else
throw new BLException("Library was left in DEBUG Mode.");
#endif
b64Data = Convert.ToBase64String(ExportToExcelAsBytes(ds));
}
catch (Exception ex)
{
Success = false;
ListErrors.Add(ex);
}
finally
{
ListErrorsJson = JsonConvert.SerializeObject(ListErrors, Formatting.Indented);
}
Function → ReadExcelFileReturnDataSet
Signature →
Name | Type |
---|---|
inServerFolder | System.Int32 |
inFileName | System.String |
hasHeader | System.Boolean |
Usings →
none
Code →
var sb = (ISandbox)Sandbox;
var serverFolder = (ServerFolder)inServerFolder;
var filePath = new FilePath(serverFolder, inFileName);
var excelFileBytes = sb.IO.File.ReadAllBytes(filePath);
var excelFileAsB64 = Convert.ToBase64String(excelFileBytes);
var processExcel = ThisLib.ExcelToDataSet(excelFileAsB64, hasHeader);
if(processExcel.Success)
{
ds = processExcel.ds;
}
Example here →