How to count the rows per sheet using OpenXML - c#

The code below only count the total number of rows in sheet 1 but I also want to count the rows in sheet 2 and sheet 3 and so on
using (SpreadsheetDocument myDoc = SpreadsheetDocument.Open("PATH", true))
{
//Get workbookpart
WorkbookPart workbookPart = myDoc.WorkbookPart;
//then access to the worksheet part
IEnumerable<WorksheetPart> worksheetPart = workbookPart.WorksheetParts;
foreach (WorksheetPart WSP in worksheetPart)
{
//find sheet data
IEnumerable<SheetData> sheetData = WSP.Worksheet.Elements<SheetData>();
// Iterate through every sheet inside Excel sheet
foreach (SheetData SD in sheetData)
{
IEnumerable<Row> row = SD.Elements<Row>(); // Get the row IEnumerator
Console.WriteLine(row.Count()); // Will give you the count of rows
}
}
}

Related

Read excel by sheet name with OpenXML

I am new at OpenXML c# and I want to read rows from excel file. But I need to read excel sheet by name. this is my sample code that reads first sheet:
using (var spreadSheet = SpreadsheetDocument.Open(path, true))
{
WorkbookPart workbookPart = spreadSheet.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
foreach (Row r in sheetData.Elements<Row>())
{
foreach (Cell c in r.Elements<Cell>())
{
if (c.DataType != null && c.DataType == CellValues.SharedString)
{
// reading cells
}
}
}
But how can I find by sheet name and read cells.
I've done it like in the code snippet below. It's basically Workbook->Spreadsheet->Sheet then getting the Name attribute of the sheet.
The basic underling xml looks like this:
<x:workbook>
<x:sheets>
<x:sheet name="Sheet1" sheetId="1" r:id="rId1" />
<x:sheet name="TEST sheet Name" sheetId="2" r:id="rId2" />
</x:sheets>
</x:workbook>
The id value is what the Open XML package uses internally to identify each sheet and link it with the other XML parts. That's why the line of code that follows identifying the name uses GetPartById to pick up the WorksheetPart.
using (SpreadsheetDocument doc = SpreadsheetDocument.Open(path, false))
{
WorkbookPart bkPart = doc.WorkbookPart;
DocumentFormat.OpenXml.Spreadsheet.Workbook workbook = bkPart.Workbook;
DocumentFormat.OpenXml.Spreadsheet.Sheet s = workbook.Descendants<DocumentFormat.OpenXml.Spreadsheet.Sheet>().Where(sht => sht.Name == "Sheet1").FirstOrDefault();
WorksheetPart wsPart = (WorksheetPart)bkPart.GetPartById(s.Id);
DocumentFormat.OpenXml.Spreadsheet.SheetData sheetdata = wsPart.Worksheet.Elements<DocumentFormat.OpenXml.Spreadsheet.SheetData>().FirstOrDefault();
foreach (DocumentFormat.OpenXml.Spreadsheet.Row r in sheetdata.Elements<DocumentFormat.OpenXml.Spreadsheet.Row>())
{
DocumentFormat.OpenXml.Spreadsheet.Cell c = r.Elements<DocumentFormat.OpenXml.Spreadsheet.Cell>().First();
txt += c.CellValue.Text + Environment.NewLine;
}
this.txtMessages.Text += txt;
}

How to parse multiple sheets in Excel to data-set by using OpenXml

I search lot about how to read and copy data in multiple Excel sheets to data-set using open XML. Only get how to parse single sheet excel to data table. I have a Excel file with multiple sheets. I want to take the data to data-set, so below I pasted the code for copying one sheet data. Please help me to read multiple sheet excel to data-set.
public static DataTable ReadAsDataTable(string fileName)
{
DataTable dataTable = new DataTable();
using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(fileName, false))
{
WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
IEnumerable<Sheet> sheets = spreadSheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
string relationshipId = sheets.First().Id.Value;
WorksheetPart worksheetPart = (WorksheetPart)spreadSheetDocument.WorkbookPart.GetPartById(relationshipId);
Worksheet workSheet = worksheetPart.Worksheet;
SheetData sheetData = workSheet.GetFirstChild<SheetData>();
IEnumerable<Row> rows = sheetData.Descendants<Row>();
foreach (Cell cell in rows.ElementAt(0))
{
dataTable.Columns.Add(GetCellValue(spreadSheetDocument, cell));
}
foreach (Row row in rows)
{
DataRow dataRow = dataTable.NewRow();
for (int i = 0; i < row.Descendants<Cell>().Count(); i++)
{
dataRow[i] = GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i));
}
dataTable.Rows.Add(dataRow);
}
}
dataTable.Rows.RemoveAt(0);
return dataTable;
}
I understand your requirement clearly and it is very simple to read multiple worksheets in excel using open xml.
you just need to add one for each loop.
I have already create a article with the same requirement in my website,kindly refer that and surely you will get a solution.
http://www.learnandshare-karthik.com/2016/08/29/open-xml-sdk-to-read-workbook-with-multiple-worksheets/
if you still need further help,please let me know
thanks
karthik

How to count rows per worksheet in OpenXML

I switched from Interop library to OpenXML, because I need to read large Excel files. Before that I could use:
worksheet.UsedRange.Rows.Count
to get the number of rows with data on the worksheet. I used this information to make a progressbar. In OpenXML I do not know how to get the same information about the worksheet. What I have now is this code:
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(path, false))
{
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
int row_count = 0, col_count;
// here I would like to get the info about the number of rows
foreach (Row r in sheetData.Elements<Row>())
{
col_count = 0;
if (row_count > 10)
{
foreach (Cell c in r.Elements<Cell>())
{
// do some stuff
// update progressbar
}
}
row_count++;
}
}
It's not that hard (When you use LINQ),
using (SpreadsheetDocument myDoc = SpreadsheetDocument.Open("PATH", true))
{
//Get workbookpart
WorkbookPart workbookPart = myDoc.WorkbookPart;
//then access to the worksheet part
IEnumerable<WorksheetPart> worksheetPart = workbookPart.WorksheetParts;
foreach (WorksheetPart WSP in worksheetPart)
{
//find sheet data
IEnumerable<SheetData> sheetData = WSP.Worksheet.Elements<SheetData>();
// Iterate through every sheet inside Excel sheet
foreach (SheetData SD in sheetData)
{
IEnumerable<Row> row = SD.Elements<Row>(); // Get the row IEnumerator
Console.WriteLine(row.Count()); // Will give you the count of rows
}
}
}
Edited with Linq now it's straight forward.

How to retrieve Tab names from excel sheet using OpenXML

I have a spreadsheet document that has 182 columns in it. I need to place the spreadsheet data into a data table, tab by tab, but i need to find out as I'm adding data from each tab, what is the tab name, and add the tab name to a column in the data table.
This is how I set up the data table.
I then loop in the workbook and drill down to the sheetData object and walk through each row and column, getting cell data.
DataTable dt = new DataTable();
for (int i = 0; i <= col.GetUpperBound(0); i++)
{
try
{
dt.Columns.Add(new DataColumn(col[i].ToString(), typeof(string)));
}
catch (Exception e)
{
MessageBox.Show("Uploader Error" + e.ToString());
return null;
}
}
dt.Columns.Add(new DataColumn("SheetName", typeof(string)));
However at the end of the string array that I use for the Data Table, I need to add the tab name. How can I find out the tab name as I'm looping in the sheet in Open XML?
Here is my code so far:
using (SpreadsheetDocument spreadSheetDocument =
SpreadsheetDocument.Open(Destination, false))
{
WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
Workbook workbook = spreadSheetDocument.WorkbookPart.Workbook;
Sheets sheets =
spreadSheetDocument
.WorkbookPart
.Workbook
.GetFirstChild<DocumentFormat.OpenXml.Spreadsheet.Sheets>();
OpenXmlElementList list = sheets.ChildElements;
foreach (WorksheetPart worksheetpart in workbook.WorkbookPart.WorksheetParts)
{
Worksheet worksheet = worksheetpart.Worksheet;
foreach (SheetData sheetData in worksheet.Elements<SheetData>())
{
foreach (Row row in sheetData.Elements())
{
string[] thisarr = new string[183];
int index = 0;
foreach (Cell cell in row.Elements())
{
thisarr[(index)] = GetCellValue(spreadSheetDocument, cell);
index++;
}
thisarr[182] = ""; //need to add tabname here
if (thisarr[0].ToString() != "")
{
dt.Rows.Add(thisarr);
}
}
}
}
}
return dt;
Just a note: I did previously get the tab names from the InnerXML property of "list" in
OpenXmlElementList list = sheets.ChildElements;
however I noticed as I'm looping in the spreadsheet it does not get the tab names in the right order.
Here is a handy helper method to get the Sheet corresponding to a WorksheetPart:
public static Sheet GetSheetFromWorkSheet
(WorkbookPart workbookPart, WorksheetPart worksheetPart)
{
string relationshipId = workbookPart.GetIdOfPart(worksheetPart);
IEnumerable<Sheet> sheets = workbookPart.Workbook.Sheets.Elements<Sheet>();
return sheets.FirstOrDefault(s => s.Id.HasValue && s.Id.Value == relationshipId);
}
Then you can get the name from the sheets Name-property:
Sheet sheet = GetSheetFromWorkSheet(myWorkbookPart, myWorksheetPart);
string sheetName = sheet.Name;
...this will be the "tab name" OP referred to.
For the record the opposite method would look like:
public static Worksheet GetWorkSheetFromSheet(WorkbookPart workbookPart, Sheet sheet)
{
var worksheetPart = (WorksheetPart)workbookPart.GetPartById(sheet.Id);
return worksheetPart.Worksheet;
}
...and with that we can also add the following method:
public static IEnumerable<KeyValuePair<string, Worksheet>> GetNamedWorksheets
(WorkbookPart workbookPart)
{
return workbookPart.Workbook.Sheets.Elements<Sheet>()
.Select(sheet => new KeyValuePair<string, Worksheet>
(sheet.Name, GetWorkSheetFromSheet(workbookPart, sheet)));
}
Now you can easily enumerate through all Worksheets including their name.
Throw it all into a dictionary for name-based lookup if you prefer that:
IDictionary<string, WorkSheet> wsDict = GetNamedWorksheets(myWorkbookPart)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
...or if you just want one specific sheet by name:
public static Sheet GetSheetFromName(WorkbookPart workbookPart, string sheetName)
{
return workbookPart.Workbook.Sheets.Elements<Sheet>()
.FirstOrDefault(s => s.Name.HasValue && s.Name.Value == sheetName);
}
(Then call GetWorkSheetFromSheet to get the corresponding Worksheet.)
The sheet names are stored in the WorkbookPart in a Sheets element which has children of element Sheet which corresponds to each worksheet in the Excel file. All you have to do is grab the correct index out of that Sheets element and that will be the Sheet you are on in your loop. I added a snippet of code below to do what you want.
int sheetIndex = 0;
foreach (WorksheetPart worksheetpart in workbook.WorkbookPart.WorksheetParts)
{
Worksheet worksheet = worksheetpart.Worksheet;
// Grab the sheet name each time through your loop
string sheetName = workbookPart.Workbook.Descendants<Sheet>().ElementAt(sheetIndex).Name;
foreach (SheetData sheetData in worksheet.Elements<SheetData>())
{
...
}
sheetIndex++;
}
Using spreadsheetDocument As SpreadsheetDocument = spreadsheetDocument.Open("D:\Libro1.xlsx", True)
Dim workbookPart As WorkbookPart = spreadsheetDocument.WorkbookPart
workbookPart.Workbook.Descendants(Of Sheet)()
Dim worksheetPart As WorksheetPart = workbookPart.WorksheetParts.Last
Dim text As String
For Each Sheet As Sheet In spreadsheetDocument.WorkbookPart.Workbook.Sheets
Dim sName As String = Sheet.Name
Dim sID As String = Sheet.Id
Dim part As WorksheetPart = workbookPart.GetPartById(sID)
Dim actualSheet As Worksheet = part.Worksheet
Dim sheetData As SheetData = part.Worksheet.Elements(Of SheetData)().First
For Each r As Row In sheetData.Elements(Of Row)()
For Each c As Cell In r.Elements(Of Cell)()
text = c.CellValue.Text
Console.Write(text & " ")
Next
Next
Next
End Using
Console.Read()
worksheet.GetAttribute("name","").Value

Is there a SAX way to loop through OpenXML rows?

I'm parsing a large file using the SAX approach offered on:
Parsing and Reading Large Excel Files with the Open XML SDK
This is my modified version (only getting the row number for simplicity)
using (SpreadsheetDocument myDoc = SpreadsheetDocument.Open("BigFile.xlsx", true))
{
WorkbookPart workbookPart = myDoc.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
OpenXmlReader reader = OpenXmlReader.Create(worksheetPart);
String rowNum;
while (reader.Read())
{
if (reader.ElementType == typeof(Row))
{
if (reader.HasAttributes)
rowNum = reader.Attributes.First(a => a.LocalName == "r").Value
}
}
}
The problem is that this loops through every item/cell/column/whatnot and only acts when the element type is Row.
Is there a SAX way to loop only through the rows and not every item in the worksheet?
Thanks,
The key is to use the Skip() and ReadNextSibling() methods of the reader...
using (SpreadsheetDocument myDoc = SpreadsheetDocument.Open("BigFile.xlsx", true))
{
WorkbookPart workbookPart = myDoc.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
OpenXmlReader reader = OpenXmlReader.Create(worksheetPart);
String rowNum;
while (reader.Read())
{
if (reader.ElementType == typeof(Row))
{
do
{
if (reader.HasAttributes)
rowNum = reader.Attributes.First(a => a.LocalName == "r").Value;
} while (reader.ReadNextSibling()); // Skip to the next row
break; // We just looped through all the rows so no need to continue reading the worksheet
}
if (reader.ElementType != typeof(Worksheet)) // Dont' want to skip the contents of the worksheet
reader.Skip(); // Skip contents of any node before finding the first row.
}
}

Categories