From 13dbca29d9082d91a47df04c468c89fe1315e405 Mon Sep 17 00:00:00 2001 From: HLWeil Date: Thu, 14 Mar 2024 14:27:42 +0100 Subject: [PATCH] finish first version of json io functions --- src/FsSpreadsheet.CsvIO/FsExtension.fs | 20 +++--- src/FsSpreadsheet.Js/FsExtensions.fs | 48 ++++++++++---- src/FsSpreadsheet.Js/FsSpreadsheet.Js.fsproj | 6 +- src/FsSpreadsheet.Js/Json.fs | 37 +++++++++++ src/FsSpreadsheet.Js/Xlsx.fs | 8 +-- src/FsSpreadsheet.Net/FsExtensions.fs | 64 ++++++++++++++----- .../FsSpreadsheet.Net.fsproj | 1 + src/FsSpreadsheet.Net/ZipArchiveReader.fs | 2 +- src/FsSpreadsheet.Py/FsExtension.fs | 39 +++++++---- src/FsSpreadsheet.Py/FsSpreadsheet.Py.fsproj | 6 +- src/FsSpreadsheet.Py/Json.fs | 26 ++++++++ src/FsSpreadsheet.Py/Xlsx.fs | 6 +- src/FsSpreadsheet/FsSpreadsheet.fsproj | 2 +- src/FsSpreadsheet/Json/Table.fs | 24 +++++++ src/FsSpreadsheet/Json/Value.fs | 2 + src/FsSpreadsheet/Json/Worksheet.fs | 16 ++++- 16 files changed, 240 insertions(+), 67 deletions(-) create mode 100644 src/FsSpreadsheet.Js/Json.fs create mode 100644 src/FsSpreadsheet.Py/Json.fs create mode 100644 src/FsSpreadsheet/Json/Table.fs diff --git a/src/FsSpreadsheet.CsvIO/FsExtension.fs b/src/FsSpreadsheet.CsvIO/FsExtension.fs index 752df39a..911775c9 100644 --- a/src/FsSpreadsheet.CsvIO/FsExtension.fs +++ b/src/FsSpreadsheet.CsvIO/FsExtension.fs @@ -64,7 +64,7 @@ module FsExtensions = |> streamWriter.Write streamWriter.Flush() - member self.ToBytes(?Separator : char) = + member self.ToXlsxBytes(?Separator : char) = use memoryStream = new System.IO.MemoryStream() match Separator with | Some s -> self.ToStream(memoryStream,s) @@ -73,8 +73,8 @@ module FsExtensions = member self.ToFile(path,?Separator : char) = match Separator with - | Some s -> self.ToBytes(s) - | None -> self.ToBytes() + | Some s -> self.ToXlsxBytes(s) + | None -> self.ToXlsxBytes() |> fun bytes -> System.IO.File.WriteAllBytes (path, bytes) static member toStream(stream : System.IO.MemoryStream,workbook : FsWorkbook,?Separator : char) = @@ -83,12 +83,12 @@ module FsExtensions = | None -> workbook.ToStream(stream) workbook.ToStream(stream) - static member toBytes(workbook: FsWorkbook,?Separator : char) = + static member toXlsxBytes(workbook: FsWorkbook,?Separator : char) = match Separator with - | Some s -> workbook.ToBytes(s) - | None -> workbook.ToBytes() + | Some s -> workbook.ToXlsxBytes(s) + | None -> workbook.ToXlsxBytes() - static member toFile(path,workbook: FsWorkbook,?Separator : char) = + static member toXlsxFile(path,workbook: FsWorkbook,?Separator : char) = match Separator with | Some s -> workbook.ToFile(path,s) | None -> workbook.ToFile(path) @@ -101,10 +101,10 @@ type Writer = | None -> workbook.ToStream(stream) workbook.ToStream(stream) - static member toBytes(workbook: FsWorkbook,?Separator : char) = + static member toXlsxBytes(workbook: FsWorkbook,?Separator : char) = match Separator with - | Some s -> workbook.ToBytes(s) - | None -> workbook.ToBytes() + | Some s -> workbook.ToXlsxBytes(s) + | None -> workbook.ToXlsxBytes() static member toFile(path,workbook: FsWorkbook,?Separator : char) = match Separator with diff --git a/src/FsSpreadsheet.Js/FsExtensions.fs b/src/FsSpreadsheet.Js/FsExtensions.fs index fe57837d..41dadc70 100644 --- a/src/FsSpreadsheet.Js/FsExtensions.fs +++ b/src/FsSpreadsheet.Js/FsExtensions.fs @@ -17,23 +17,43 @@ type FsWorkbook with static member fromXlsxStream(stream:System.IO.Stream) : Promise = Xlsx.fromXlsxStream stream - static member fromBytes(bytes: byte []) : Promise = - Xlsx.fromBytes bytes + static member fromXlsxBytes(bytes: byte []) : Promise = + Xlsx.fromXlsxBytes bytes - static member toFile(path: string) (wb:FsWorkbook) : Promise = - Xlsx.toFile path wb + static member toXlsxFile(path: string) (wb:FsWorkbook) : Promise = + Xlsx.toXlsxFile path wb - static member toStream(stream: System.IO.Stream) (wb:FsWorkbook) : Promise = - Xlsx.toStream stream wb + static member toXlsxStream(stream: System.IO.Stream) (wb:FsWorkbook) : Promise = + Xlsx.toXlsxStream stream wb - static member toBytes(wb:FsWorkbook) : Promise = - Xlsx.toBytes wb + static member toXlsxBytes(wb:FsWorkbook) : Promise = + Xlsx.toXlsxBytes wb - member this.ToFile(path: string) : Promise = - FsWorkbook.toFile path this + member this.ToXlsxFile(path: string) : Promise = + FsWorkbook.toXlsxFile path this - member this.ToStream(stream: System.IO.Stream) : Promise = - FsWorkbook.toStream stream this + member this.ToXlsxStream(stream: System.IO.Stream) : Promise = + FsWorkbook.toXlsxStream stream this - member this.ToBytes() : Promise = - FsWorkbook.toBytes this \ No newline at end of file + member this.ToXlsxBytes() : Promise = + FsWorkbook.toXlsxBytes this + + + + static member fromJsonString (json:string) : FsWorkbook = + Json.fromJsonString json + + static member toJsonString (wb:FsWorkbook) : string = + Json.toJsonString wb + + //static member fromJsonFile (path:string) : Promise = + // Json.fromJsonFile path + + //static member toJsonFile (path:string) (wb:FsWorkbook) : Promise = + // Json.toJsonFile path wb + + //member this.ToJsonFile(path: string) : Promise = + // FsWorkbook.toJsonFile path this + + member this.ToJsonString() : string = + FsWorkbook.toJsonString this \ No newline at end of file diff --git a/src/FsSpreadsheet.Js/FsSpreadsheet.Js.fsproj b/src/FsSpreadsheet.Js/FsSpreadsheet.Js.fsproj index f380206c..adbfd0d2 100644 --- a/src/FsSpreadsheet.Js/FsSpreadsheet.Js.fsproj +++ b/src/FsSpreadsheet.Js/FsSpreadsheet.Js.fsproj @@ -26,20 +26,20 @@ + + - - - + diff --git a/src/FsSpreadsheet.Js/Json.fs b/src/FsSpreadsheet.Js/Json.fs new file mode 100644 index 00000000..184a6450 --- /dev/null +++ b/src/FsSpreadsheet.Js/Json.fs @@ -0,0 +1,37 @@ +namespace FsSpreadsheet.Js + +open FsSpreadsheet +open Fable.ExcelJs +open Fable.Core +open Fable.Core.JsInterop +open Fable.Core.JS + +open Thoth.Json.JavaScript + + + + +/// This does currently not correctly work if you want to use this from js +/// https://github.com/fable-compiler/Fable/issues/3498 +[] +type Json = + + static member fromJsonString (json:string) : FsWorkbook = + match Thoth.Json.JavaScript.Decode.fromString FsSpreadsheet.Json.Workbook.decode json with + | Ok wb -> wb + | Error e -> failwithf "Could not deserialize json Workbook: \n%s" e + + static member toJsonString (wb:FsWorkbook, ?spaces) : string = + let spaces = defaultArg spaces 2 + FsSpreadsheet.Json.Workbook.encode wb + |> Thoth.Json.JavaScript.Encode.toString spaces + + //static member fromJsonFile (path:string) : Promise = + // promise { + // let! json = Fable.Core.JS. path + // return Json.fromJsonString json + // } + + //static member toJsonFile (path:string) (wb:FsWorkbook) : Promise = + // let json = Json.toJsonString wb + // Fable.Core.JS.writeFile path json \ No newline at end of file diff --git a/src/FsSpreadsheet.Js/Xlsx.fs b/src/FsSpreadsheet.Js/Xlsx.fs index a3d6206b..6f668957 100644 --- a/src/FsSpreadsheet.Js/Xlsx.fs +++ b/src/FsSpreadsheet.Js/Xlsx.fs @@ -25,7 +25,7 @@ type Xlsx = return JsWorkbook.readToFsWorkbook wb } - static member fromBytes (bytes: byte []) : Promise = + static member fromXlsxBytes (bytes: byte []) : Promise = promise { let wb = ExcelJs.Excel.Workbook() let uint8 = Fable.Core.JS.Constructors.Uint8Array.Create bytes @@ -33,15 +33,15 @@ type Xlsx = return JsWorkbook.readToFsWorkbook wb } - static member toFile (path: string) (wb:FsWorkbook) : Promise = + static member toXlsxFile (path: string) (wb:FsWorkbook) : Promise = let jswb = JsWorkbook.writeFromFsWorkbook wb jswb.xlsx.writeFile(path) - static member toStream (stream: System.IO.Stream) (wb:FsWorkbook) : Promise = + static member toXlsxStream (stream: System.IO.Stream) (wb:FsWorkbook) : Promise = let jswb = JsWorkbook.writeFromFsWorkbook wb jswb.xlsx.write(stream) - static member toBytes (wb:FsWorkbook) : Promise = + static member toXlsxBytes (wb:FsWorkbook) : Promise = promise { let jswb = JsWorkbook.writeFromFsWorkbook wb let buffer = jswb.xlsx.writeBuffer() diff --git a/src/FsSpreadsheet.Net/FsExtensions.fs b/src/FsSpreadsheet.Net/FsExtensions.fs index d827cd2f..eb981feb 100644 --- a/src/FsSpreadsheet.Net/FsExtensions.fs +++ b/src/FsSpreadsheet.Net/FsExtensions.fs @@ -308,7 +308,7 @@ module FsExtensions = /// /// Creates an FsWorkbook from a given Stream to an XlsxFile. /// - static member fromBytes (bytes : byte []) = + static member fromXlsxBytes (bytes : byte []) = let stream = new MemoryStream(bytes,writable = true) FsWorkbook.fromXlsxStream stream @@ -317,8 +317,17 @@ module FsExtensions = /// static member fromXlsxFile (filePath : string) = let bytes = File.ReadAllBytes filePath - FsWorkbook.fromBytes bytes + FsWorkbook.fromXlsxBytes bytes + static member fromJsonString (json : string) = + match Thoth.Json.Newtonsoft.Decode.fromString FsSpreadsheet.Json.Workbook.decode json with + | Ok wb -> wb + | Error e -> failwithf "Could not deserialize json Workbook: \n%s" e + + + static member fromJsonFile (filePath : string) = + let json = File.ReadAllText filePath + FsWorkbook.fromJsonString json member self.ToEmptySpreadsheet(doc : Packaging.SpreadsheetDocument) = @@ -334,7 +343,7 @@ module FsExtensions = /// /// Writes the FsWorkbook into a given MemoryStream. /// - member self.ToStream(stream : MemoryStream) = + member self.ToXlsxStream(stream : MemoryStream) = if self.GetWorksheets() |> Seq.isEmpty then failwith "Cannot write an empty workbook to a stream. Workbook did not contain any Worksheets." let doc = Spreadsheet.initEmptyOnStream stream @@ -348,28 +357,28 @@ module FsExtensions = /// /// Writes an FsWorkbook into a given MemoryStream. /// - static member toStream stream (workbook : FsWorkbook) = - workbook.ToStream stream + static member toXlsxStream stream (workbook : FsWorkbook) = + workbook.ToXlsxStream stream /// /// Returns the FsWorkbook in the form of a byte array. /// - member self.ToBytes() = + member self.ToXlsxBytes() = use memoryStream = new MemoryStream() - self.ToStream(memoryStream) + self.ToXlsxStream(memoryStream) memoryStream.ToArray() /// /// Returns an FsWorkbook in the form of a byte array. /// - static member toBytes (workbook: FsWorkbook) = - workbook.ToBytes() + static member toXlsxBytes (workbook: FsWorkbook) = + workbook.ToXlsxBytes() /// /// Writes the FsWorkbook into a binary file at the given path. /// member self.ToXlsxFile(path) = - self.ToBytes() + self.ToXlsxBytes() |> fun bytes -> File.WriteAllBytes (path, bytes) /// @@ -378,25 +387,48 @@ module FsExtensions = static member toXlsxFile path (workbook : FsWorkbook) = workbook.ToXlsxFile(path) + static member toJsonString (workbook : FsWorkbook, ?spaces) = + let spaces = defaultArg spaces 2 + FsSpreadsheet.Json.Workbook.encode workbook + |> Thoth.Json.Newtonsoft.Encode.toString spaces + + static member toJsonFile (path, ?spaces) = + fun workbook -> + let json = FsWorkbook.toJsonString (workbook,?spaces = spaces) + File.WriteAllText(path,json) + + member this.ToJsonString(?spaces) = + FsWorkbook.toJsonString(this, ?spaces = spaces) + + member this.ToJsonFile(path: string, ?spaces) = + FsWorkbook.toJsonFile(path, ?spaces = spaces) this type Writer = + /// /// Writes an FsWorkbook into a given MemoryStream. /// - static member toStream(stream : MemoryStream, workbook : FsWorkbook) = - workbook.ToStream(stream) + static member toXlsxStream(stream : MemoryStream, workbook : FsWorkbook) = + workbook.ToXlsxStream(stream) /// /// Returns an FsWorkbook in the form of a byte array. /// - static member toBytes(workbook: FsWorkbook) = - workbook.ToBytes() + static member toXlsxBytes(workbook: FsWorkbook) = + workbook.ToXlsxBytes() /// /// Writes an FsWorkbook into a binary file at the given path. /// - static member toFile(path,workbook: FsWorkbook) = - workbook.ToXlsxFile(path) \ No newline at end of file + static member toXlsxFile(path,workbook: FsWorkbook) = + workbook.ToXlsxFile(path) + + + static member toJsonString (workbook : FsWorkbook, ?spaces) = + FsWorkbook.toJsonString(workbook, ?spaces = spaces) + + static member toJsonFile (path, ?spaces) = + fun workbook -> FsWorkbook.toJsonFile (path, ?spaces = spaces) workbook \ No newline at end of file diff --git a/src/FsSpreadsheet.Net/FsSpreadsheet.Net.fsproj b/src/FsSpreadsheet.Net/FsSpreadsheet.Net.fsproj index 5ebaf83c..ef666692 100644 --- a/src/FsSpreadsheet.Net/FsSpreadsheet.Net.fsproj +++ b/src/FsSpreadsheet.Net/FsSpreadsheet.Net.fsproj @@ -35,6 +35,7 @@ + diff --git a/src/FsSpreadsheet.Net/ZipArchiveReader.fs b/src/FsSpreadsheet.Net/ZipArchiveReader.fs index 3a1d24dc..6a8ab58a 100644 --- a/src/FsSpreadsheet.Net/ZipArchiveReader.fs +++ b/src/FsSpreadsheet.Net/ZipArchiveReader.fs @@ -329,7 +329,7 @@ module FsWorkbook = use zip = new ZipArchive(stream) fromZipArchive zip - let fromBytes (bytes : byte []) = + let fromXlsxBytes (bytes : byte []) = use ms = new MemoryStream(bytes) fromStream ms diff --git a/src/FsSpreadsheet.Py/FsExtension.fs b/src/FsSpreadsheet.Py/FsExtension.fs index f3b9b8c4..7cd24a95 100644 --- a/src/FsSpreadsheet.Py/FsExtension.fs +++ b/src/FsSpreadsheet.Py/FsExtension.fs @@ -3,7 +3,6 @@ module FsSpreadsheet.Py.FsSpreadsheet open FsSpreadsheet open FsSpreadsheet.Py -open Fable.Core // This is mainly used for fsharp based access in a fable environment. // If you want to use these bindings from js, you should use the ones in `Xlsx.fs` @@ -15,24 +14,42 @@ type FsWorkbook with static member fromXlsxStream(stream:System.IO.Stream) : FsWorkbook = Xlsx.fromXlsxStream stream - static member fromBytes(bytes: byte []) : FsWorkbook = - Xlsx.fromBytes bytes + static member fromXlsxBytes(bytes: byte []) : FsWorkbook = + Xlsx.fromXlsxBytes bytes - static member toFile(path: string) (wb:FsWorkbook) : unit = - Xlsx.toFile path wb + static member toXlsxFile(path: string) (wb:FsWorkbook) : unit = + Xlsx.toXlsxFile path wb //static member toStream(stream: System.IO.Stream) (wb:FsWorkbook) : Promise = // PyWorkbook.fromFsWorkbook wb // |> fun wb -> Xlsx.writeBuffer(wb,stream) - static member toBytes(wb:FsWorkbook) : byte [] = - Xlsx.toBytes wb + static member toXlsxBytes(wb:FsWorkbook) : byte [] = + Xlsx.toXlsxBytes wb - member this.ToFile(path: string) : unit = - FsWorkbook.toFile path this + member this.ToXlsxFile(path: string) : unit = + FsWorkbook.toXlsxFile path this //member this.ToStream(stream: System.IO.Stream) : unit = // FsWorkbook.toStream stream this - member this.ToBytes() : byte [] = - FsWorkbook.toBytes this \ No newline at end of file + member this.ToXlsxBytes() : byte [] = + FsWorkbook.toXlsxBytes this + + static member fromJsonString (json:string) : FsWorkbook = + Json.fromJsonString json + + static member toJsonString (wb:FsWorkbook) : string = + Json.toJsonString wb + + //static member fromJsonFile (path:string) : Promise = + // Json.fromJsonFile path + + //static member toJsonFile (path:string) (wb:FsWorkbook) : Promise = + // Json.toJsonFile path wb + + //member this.ToJsonFile(path: string) : Promise = + // FsWorkbook.toJsonFile path this + + member this.ToJsonString() : string = + FsWorkbook.toJsonString this \ No newline at end of file diff --git a/src/FsSpreadsheet.Py/FsSpreadsheet.Py.fsproj b/src/FsSpreadsheet.Py/FsSpreadsheet.Py.fsproj index 3b300a7a..243da25a 100644 --- a/src/FsSpreadsheet.Py/FsSpreadsheet.Py.fsproj +++ b/src/FsSpreadsheet.Py/FsSpreadsheet.Py.fsproj @@ -12,17 +12,17 @@ + + - - - + diff --git a/src/FsSpreadsheet.Py/Json.fs b/src/FsSpreadsheet.Py/Json.fs new file mode 100644 index 00000000..504e3a68 --- /dev/null +++ b/src/FsSpreadsheet.Py/Json.fs @@ -0,0 +1,26 @@ +namespace FsSpreadsheet.Py + +open FsSpreadsheet +open Fable.Core +open Fable.Core.JsInterop +open Fable.Core.JS + +open Thoth.Json.Python + + + + +/// This does currently not correctly work if you want to use this from js +/// https://github.com/fable-compiler/Fable/issues/3498 +[] +type Json = + + static member fromJsonString (json:string) : FsWorkbook = + match Decode.fromString FsSpreadsheet.Json.Workbook.decode json with + | Ok wb -> wb + | Error e -> failwithf "Could not deserialize json Workbook: \n%s" e + + static member toJsonString (wb:FsWorkbook, ?spaces) : string = + let spaces = defaultArg spaces 2 + FsSpreadsheet.Json.Workbook.encode wb + |> Encode.toString spaces diff --git a/src/FsSpreadsheet.Py/Xlsx.fs b/src/FsSpreadsheet.Py/Xlsx.fs index b0682358..4b412b1e 100644 --- a/src/FsSpreadsheet.Py/Xlsx.fs +++ b/src/FsSpreadsheet.Py/Xlsx.fs @@ -19,11 +19,11 @@ type Xlsx = Xlsx.load stream |> PyWorkbook.toFsWorkbook - static member fromBytes(bytes: byte []) : FsWorkbook = + static member fromXlsxBytes(bytes: byte []) : FsWorkbook = Xlsx.read bytes |> PyWorkbook.toFsWorkbook - static member toFile(path: string) (wb:FsWorkbook) : unit = + static member toXlsxFile(path: string) (wb:FsWorkbook) : unit = PyWorkbook.fromFsWorkbook wb |> fun wb -> Xlsx.writeFile(wb,path) @@ -31,6 +31,6 @@ type Xlsx = // PyWorkbook.fromFsWorkbook wb // |> fun wb -> Xlsx.writeBuffer(wb,stream) - static member toBytes(wb:FsWorkbook) : byte [] = + static member toXlsxBytes(wb:FsWorkbook) : byte [] = PyWorkbook.fromFsWorkbook wb |> fun wb -> Xlsx.write(wb) diff --git a/src/FsSpreadsheet/FsSpreadsheet.fsproj b/src/FsSpreadsheet/FsSpreadsheet.fsproj index 097ac534..be3e97f8 100644 --- a/src/FsSpreadsheet/FsSpreadsheet.fsproj +++ b/src/FsSpreadsheet/FsSpreadsheet.fsproj @@ -36,6 +36,7 @@ + @@ -53,7 +54,6 @@ - diff --git a/src/FsSpreadsheet/Json/Table.fs b/src/FsSpreadsheet/Json/Table.fs new file mode 100644 index 00000000..3514660b --- /dev/null +++ b/src/FsSpreadsheet/Json/Table.fs @@ -0,0 +1,24 @@ +module FsSpreadsheet.Json.Table + +open FsSpreadsheet +open Thoth.Json.Core + + +[] +let name = "name" + +[] +let range = "range" + +let encode (sheet:FsTable) = + Encode.object [ + name, Encode.string sheet.Name + range, Encode.string sheet.RangeAddress.Range + ] + +let decode : Decoder = + Decode.object (fun builder -> + let n = builder.Required.Field name Decode.string + let r = builder.Required.Field range Decode.string + FsTable(n, FsRangeAddress(r)) + ) \ No newline at end of file diff --git a/src/FsSpreadsheet/Json/Value.fs b/src/FsSpreadsheet/Json/Value.fs index f9291134..f75e2bec 100644 --- a/src/FsSpreadsheet/Json/Value.fs +++ b/src/FsSpreadsheet/Json/Value.fs @@ -8,6 +8,7 @@ let encode (value : obj) = | :? int as i -> Encode.int i | :? float as f -> Encode.float f | :? bool as b -> Encode.bool b + | :? System.DateTime as d -> Encode.datetime d | _ -> Encode.nil let decode : Decoder = @@ -16,4 +17,5 @@ let decode : Decoder = Decode.int |> Decode.map (fun i -> i :> obj) Decode.float |> Decode.map (fun f -> f :> obj) Decode.bool |> Decode.map (fun b -> b :> obj) + Decode.datetimeLocal |> Decode.map (fun d -> d :> obj) ] \ No newline at end of file diff --git a/src/FsSpreadsheet/Json/Worksheet.fs b/src/FsSpreadsheet/Json/Worksheet.fs index 337e9a79..f0e0b775 100644 --- a/src/FsSpreadsheet/Json/Worksheet.fs +++ b/src/FsSpreadsheet/Json/Worksheet.fs @@ -9,17 +9,24 @@ let name = "name" [] let rows = "rows" +[] +let tables = "tables" + let encode (sheet:FsWorksheet) = Encode.object [ name, Encode.string sheet.Name + if Seq.isEmpty sheet.Tables |> not then + tables, Encode.seq (sheet.Tables |> Seq.map Table.encode) rows, Encode.seq (sheet.Rows |> Seq.map Row.encode) + ] let decode : Decoder = Decode.object (fun builder -> let n = builder.Required.Field name Decode.string + let ts = builder.Optional.Field tables (Decode.seq Table.decode) let rs = builder.Required.Field rows (Decode.seq Row.decode) - let sheet = new FsWorksheet(name) + let sheet = new FsWorksheet(n) rs |> Seq.iteri (fun rowI cells -> let r = sheet.Row(rowI + 1) @@ -28,5 +35,12 @@ let decode : Decoder = r[coli + 1].Value <- cell.Value ) ) + match ts with + | Some ts -> + ts + |> Seq.iter (fun t -> + sheet.AddTable(t) |> ignore + ) + | None -> () sheet ) \ No newline at end of file