Невозможно добавить лист, используя OpenXml с F # (FSharp) - PullRequest
7 голосов
/ 18 апреля 2011

Пример метода CreateSpreadsheetWorkbook из документации OpenXml транслируется непосредственно в F #.Кажется, проблема в методе Append объекта Sheets.Код выполняется без ошибок, но в результирующем файле xlsx отсутствует внутренний Xml, который должен был быть добавлен, и файл не читается Excel.Я подозреваю, что проблема связана с преобразованием функциональных структур F # в тип System.Collections, но у меня нет прямых доказательств этого.

Я запустил подобный код в C # и VB.NET (т.е. документацияпример), и он отлично выполняется и создает читаемый, полный файл xlsx.

Я знаю, что мог бы иметь дело с XML напрямую, но я хотел бы понять природу несоответствия между F # и OpenXml.Любые предложения?

Код почти прямо из примера:

namespace OpenXmlLib
open System
open DocumentFormat
open DocumentFormat.OpenXml
open DocumentFormat.OpenXml.Packaging 
open DocumentFormat.OpenXml.Spreadsheet

module OpenXmlXL = 
   // this function overwrites an existing file without warning!
   let CreateSpreadsheetWorkbook (filepath: string) = 
      // Create a spreadsheet document by supplying the filepath.
      // By default, AutoSave = true, Editable = true, and Type = xlsx.
      let spreadsheetDocument = SpreadsheetDocument.Create(filepath, SpreadsheetDocumentType.Workbook)
      // Add a WorkbookPart to the document.
      let workbookpart = spreadsheetDocument.AddWorkbookPart()
      workbookpart.Workbook <- new Workbook()

      // Add a WorksheetPart to the WorkbookPart.
      let worksheetPart = workbookpart.AddNewPart<WorksheetPart>()
      worksheetPart.Worksheet <- new Worksheet(new SheetData())

      // Add Sheets to the Workbook.
      let sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets()) 

      // Append a new worksheet and associate it with the workbook.
      let sheet = new Sheet() 
      sheet.Id <-  stringValue(spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart))
      //Console.WriteLine(sheet.Id.Value)

      sheet.SheetId <-  UInt32Value(1u)
      // Console.WriteLine(sheet.SheetId.Value)

      sheet.Name <-  StringValue("TestSheet")
      //Console.WriteLine(sheet.Name.Value)


      sheets.Append (sheet)

     // Console.WriteLine("Sheets: {0}", sheets.InnerXml.ToString())

      workbookpart.Workbook.Save()


      spreadsheetDocument.Close()

Лист создан, но пустой:

sheet.xml:

  <?xml version="1.0" encoding="utf-8" ?> 
  <x:worksheet xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main" />

workbook.xml:
  <?xml version="1.0" encoding="utf-8" ?> 
- <x:workbook xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
- <x:sheets>
  <x:sheet name="TestSheet" sheetId="1" r:id="R263eb6f245a2497e" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" /> 
  </x:sheets>
  </x:workbook>

1 Ответ

16 голосов
/ 18 апреля 2011

Проблема очень тонкая и заключается в ваших вызовах конструктора Worksheet и метода Sheets.Append.Оба эти метода перегружены и могут принимать seq<OpenXmlElement> или любое количество отдельных OpenXmlElement с (через массив [<System.ParamArray>] / params).Суть в том, что сам тип OpenXmlElement реализует интерфейс seq<OpenXmlElement>.

В C #, когда вы вызываете new Worksheet(new SheetData()), разрешение перегрузки компилятора выбирает вторую из перегрузок, неявно создавая один элементмассив, содержащий значение SheetData.Однако в F #, поскольку класс SheetData реализует IEnumerable<OpenXmlElement>, выбирается первая перегрузка, которая создает новый WorkSheet путем перечисления содержимого SheetData, что не является тем, что выхочу.

Чтобы исправить это, вам нужно настроить вызовы так, чтобы они использовали другую перегрузку (первый пример ниже) или явно создать одноэлементную последовательность (второй пример ниже):

worksheetPart.Worksheet <- new Worksheet(new SheetData() :> OpenXmlElement)
...
sheets.Append([sheet :> OpenXmlElement])
...