Leer tablas de un Excel con Open XML y Linq



Hace bastante tiempo escribí un post donde explicaba cómo era posible la creación de un archivo Excel con Open XML utilizando una base de datos como fuente. Esta vez, voy a comentar cómo podemos leer datos de una hoja para poder manipularlos desde C#.


Antes de comenzar, necesitamos utilizar las referencias Windows Base y DocumentFormat.OpenXml. En este ejemplo, vamos a recuperar los siguientes valores:



La información más importante de este archivo para poder recuperar correctamente los datos son el nombre de la hoja y el número de la fila donde comienzan los datos que nos interesan.


El objetivo de nuestro código será recuperar estos datos y almacenarlos en un txt de forma formateada. Creamos un proyecto de consola y comenzamos con el siguiente código:

using System.IO;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

namespace ExcelOpenXML
{
    class Program
    {
        static void Main()
        {

            using (var document = SpreadsheetDocument.Open(@»C:BooksSample.xlsx», true))
            {

                var workbook = document.WorkbookPart.Workbook;
                var workSheets = workbook.Descendants<Sheet>();

                string sheetId = workSheets.First(s => s.Name == @»Hoja1″).Id;
                var sheet = (WorksheetPart)document.WorkbookPart.GetPartById(sheetId);

                var sharedStringTable = document.WorkbookPart.SharedStringTablePart.SharedStringTable;
                var books = Library.RetrieveBooks(sheet.Worksheet, sharedStringTable);
                using (var sw = new StreamWriter(@»c:Books.txt»))
                {
                    foreach (var p in from book in books select book)
                        sw.WriteLine(«{0, -40} {1, -20} «, p.Name, p.Price);
                }

            }
        }
    }
}


Si nos fijamos en el código anterior, una vez cargado el archivo tenemos que acceder al objeto workbook, solicitamos todas las hojas en orden descendente y recuperamos el id de aquella que tenga como nombre Hoja1. Con este id podemos recuperar la hoja en cuestión y, por otro lado, recuperamos un elemento llamado SharedStringTable, el cual contiene un conjunto de elementos donde están alojadas todas las cadenas utilizadas en la hoja. Para que os hagáis una idea de lo que estamos recuperando, podemos abrir el archivo con Open XML SDK 2.0 Productivity Tool for Microsoft Office y visualizar su esquema:



¿Cómo recuperamos la información entre tanto XML? :)

using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using DocumentFormat.OpenXml.Spreadsheet;

namespace ExcelOpenXML
{
    public class Library
    {
        static readonly CultureInfo Culture = new CultureInfo(«en-US»);

        public static List<Book> RetrieveBooks(Worksheet worksheet, SharedStringTable sharedStringTable)
        {
            var result = new List<Book>();

            var dataRows = from row in worksheet.Descendants<Row>()
                           where row.RowIndex > 6 //The table begins on line 5
                           select row;

            foreach (var row in dataRows)
            {
                var textValues = new List<string>();
                foreach (var cell in from cell in row.Descendants<Cell>() where cell.CellValue != null select cell)
                {
                    if (cell.DataType != null && cell.DataType.HasValue && cell.DataType == CellValues.SharedString)
                        textValues.Add(sharedStringTable.ChildElements[int.Parse(cell.CellValue.InnerText)].InnerText);
                    else
                        textValues.Add(cell.CellValue.InnerText);
                }

                var textArray = textValues.ToArray();

                if (textArray.Count() <= 0) continue;

                var book = new Book
                {
                    Name = textArray[0],
                    Price = textArray[1] == «?» ? 0 : float.Parse(textArray[1], NumberStyles.Float, Culture),
                };

                result.Add(book);
            }

            return result;

        }
    }
}


Aunque la tabla comience en la fila 5, realmente nos interesa recuperar solamente los datos y obviar la cabecera. Una vez recuperados todos los registros, recorremos cada uno de ellos para poder almacenar la información en el objeto Book, el cual sólo contiene dos propiedades: Name y Price.


Un dato importante es utilizar la cultura en-US ya que, al recuperar los valores del archivo, el SDK de Open XML recupera los valores con punto en vez de coma. Este sería el resultado con el parseo pero sin la cultura en-US.

Introducing HTML5 (Voices That Matter)   1,756E+16
JavaScript: The Good Parts               1383
CSS: The Missing Manual                  1,756E+16
JavaScript: The Definitive Guide         2371
Programming in Objective-C 2.0           2223

Si ejecutamos la aplicación, abrirá el archivo xlsx y, una vez cargados en memoria, escribirá uno a uno en el archivo Books.txt. El resultado sería el siguiente:

Introducing HTML5 (Voices That Matter)   17,56
JavaScript: The Good Parts               13,83
CSS: The Missing Manual                  17,56
JavaScript: The Definitive Guide         23,71
Programming in Objective-C 2.0           22,23

Adjunto el proyecto por si fuera de utilidad :D



¡Saludos!

2 comentarios sobre “Leer tablas de un Excel con Open XML y Linq”

  1. Hola rolando,

    Muchas gracias por tu comentario 🙂

    Si bien este formato se introdujo a partir de Office 2007, podemos hacer uso del mismo en versiones anteriores (Microsoft Office 2000, Office XP y Office 2003) con un pack que salió de compatibilidad. Te adjunto el link por si te fuera de utilidad:

    http://office.microsoft.com/en-us/support/microsoft-office-compatibility-pack-for-word-excel-and-powerpoint-2007-file-formats-HA010168676.aspx

    ¡Saludos!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *