Sheets in .NET with Jint
Jint1 is a JavaScript interpreter for .NET Standard and .NET Core. It has
built-in support for binary data with .NET byte[]
and ES6 Uint8Array
.
SheetJS is a JavaScript library for reading and writing data from spreadsheets.
This demo uses Jint and SheetJS to read and write spreadsheets. We'll explore how to load SheetJS in the Jint engine, exchange binary data with a C# program, and process spreadsheets and structured data.
The "Integration Example" section includes a complete command-line tool for reading arbitrary workbooks and writing data to XLSB (Excel 2007+ Binary Format) workbooks.
The dotnet
command embeds telemetry.
The DOTNET_CLI_TELEMETRY_OPTOUT
environment variable should be set to 1
.
"Platform Configuration" includes instructions for setting the environment variable on supported platforms.
Integration Details
Most of the integration functions are not documented. This explanation is based
on version 3.1.0
.
The SheetJS Standalone scripts can be parsed and evaluated in a Jint engine instance.
Initialize Jint
A Jint.Engine
object can be created in one line:
var engine = new Jint.Engine();
Jint does not expose the NodeJS global
but does provide globalThis
. Using
Jint.Engine#Evaluate
, a global
global variable can be created:
The JS code is
global = globalThis;
engine.Evaluate("global = globalThis");
Load SheetJS Scripts
The main library can be loaded by reading the scripts from the file system with
System.IO.File.ReadAllText
and evaluating in the Jint engine instance:
/* read and evaluate the shim script */
string src = System.IO.File.ReadAllText("shim.min.js");
engine.Evaluate(src);
/* read and evaluate the main library */
engine.Evaluate(System.IO.File.ReadAllText("xlsx.full.min.js"));
To confirm the library is loaded, XLSX.version
can be inspected:
Console.WriteLine("SheetJS version {0}", engine.Evaluate("XLSX.version"));
The Jint Evaluate
method returns a generic Jint.Native.JsValue
object.
When the JS expression returns a string, the JsValue
object is an instance of
Jint.Native.JsString
. C# ToString
will return the underlying string
value.
Reading Files
In C#, System.IO.File.ReadAllBytes
reads file data into a byte[]
byte array:
string filename = "pres.xlsx";
byte[] buf = File.ReadAllBytes(filename);
Jint natively supports Uint8Array
construction from the byte array:
Jint.Native.JsValue u8 = engine.Intrinsics.Uint8Array.Construct(buf);
Jint.Engine#SetValue
will assign the Uint8Array
to a scope variable in JS:
engine.SetValue("buf", u8);
The buf
variable can be parsed from JS with the SheetJS read
method2:
The JS code is
var wb = XLSX.read(buf);
engine.Evaluate("var wb = XLSX.read(buf);");
wb
is a SheetJS workbook object. The "SheetJS Data Model" section
describes the object structure and the "API Reference" section
describes various helper functions.
Writing Files
The SheetJS write
method3 can write workbooks. The option type: "buffer"
instructs the library to generate Uint8Array
objects.
The JS code for exporting to the XLSB format is:
var u8 = XLSX.write(wb, {bookType: 'xlsb', type: 'buffer'});
The file format can be changed with the bookType
option4
Jint.Native.JsValue xlsb = engine.Evaluate("XLSX.write(wb, {bookType: 'xlsb', type: 'buffer'})");
xlsb
represents a Uint8Array
. xlsb.AsUint8Array()
returns the bytes as a
byte[]
array which can be exported with System.IO.File.WriteAllBytes
:
byte[] outfile = xlsb.AsUint8Array();
System.IO.File.WriteAllBytes("SheetJSJint.xlsb", outfile);
Integration Example
This demo was tested in the following deployments:
Architecture | Jint | Date |
---|---|---|
darwin-x64 | 3.0.1 | 2024-03-15 |
darwin-arm | 3.1.2 | 2024-05-25 |
win10-x64 | 3.1.0 | 2024-04-17 |
win11-arm | 3.1.2 | 2024-05-25 |
linux-x64 | 3.1.0 | 2024-04-25 |
linux-arm | 3.1.2 | 2024-05-25 |
Platform Configuration
- Set the
DOTNET_CLI_TELEMETRY_OPTOUT
environment variable to1
.
How to disable telemetry (click to hide)
- Linux/MacOS
- Windows
Add the following line to .profile
, .bashrc
and .zshrc
:
export DOTNET_CLI_TELEMETRY_OPTOUT=1
Close and restart the Terminal to load the changes.
Type env
in the search bar and select "Edit the system environment variables".
In the new window, click the "Environment Variables..." button.
In the new window, look for the "System variables" section and click "New..."
Set the "Variable name" to DOTNET_CLI_TELEMETRY_OPTOUT
and the value to 1
.
Click "OK" in each window (3 windows) and restart your computer.
- Install .NET
Installation Notes (click to show)
For macOS x64 and ARM64, install the dotnet-sdk
Cask with Homebrew:
brew install --cask dotnet-sdk
For Steam Deck Holo and other Arch Linux x64 distributions, the dotnet-sdk
and
dotnet-runtime
packages should be installed using pacman
:
sudo pacman -Syu dotnet-sdk dotnet-runtime
https://dotnet.microsoft.com/en-us/download/dotnet/6.0 is the official source for Windows and ARM64 Linux versions.
- Open a new Terminal window in macOS or PowerShell window in Windows.
Base Project
- Create a new folder
SheetJSJint
and a new project using thedotnet
tool:
mkdir SheetJSJint
cd SheetJSJint
dotnet new console
dotnet run
- Add Jint using the NuGet tool:
dotnet nuget add source https://www.myget.org/F/jint/api/v3/index.json
dotnet add package Jint --version 3.1.2
To verify Jint is installed, replace Program.cs
with the following:
var engine = new Jint.Engine();
Console.WriteLine("Hello {0}", engine.Evaluate("'Sheet' + 'JS'"));
After saving, run the program:
dotnet run
The terminal should display Hello SheetJS
Add SheetJS
- Download the SheetJS Standalone script, shim script and test file. Move all three files to the project directory:
curl -LO https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/shim.min.js
curl -LO https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js
curl -LO https://docs.sheetjs.com/pres.xlsx
- Replace
Program.cs
with the following:
var engine = new Jint.Engine();
engine.Evaluate("global = globalThis;");
engine.Evaluate(File.ReadAllText("shim.min.js"));
engine.Evaluate(File.ReadAllText("xlsx.full.min.js"));
Console.WriteLine("SheetJS version {0}", engine.Evaluate("XLSX.version"));
After saving, run the program:
dotnet run
The terminal should display SheetJS version 0.20.3
Read and Write Files
- Replace
Program.cs
with the following:
using Jint;
/* Initialize Jint */
var engine = new Jint.Engine();
engine.Evaluate("global = globalThis;");
/* Load SheetJS Scripts */
engine.Evaluate(File.ReadAllText("shim.min.js"));
engine.Evaluate(File.ReadAllText("xlsx.full.min.js"));
Console.WriteLine("SheetJS version {0}", engine.Evaluate("XLSX.version"));
/* Read and Parse File */
byte[] filedata = File.ReadAllBytes(args[0]);
Jint.Native.JsValue u8 = engine.Intrinsics.Uint8Array.Construct(filedata);
engine.SetValue("buf", u8);
engine.Evaluate("var wb = XLSX.read(buf);");
/* Print CSV of first worksheet*/
engine.Evaluate("var ws = wb.Sheets[wb.SheetNames[0]];");
Jint.Native.JsValue csv = engine.Evaluate("XLSX.utils.sheet_to_csv(ws)");
Console.Write(csv);
/* Generate XLSB file and save to SheetJSJint.xlsb */
Jint.Native.JsValue xlsb = engine.Evaluate("XLSX.write(wb, {bookType: 'xlsb', type: 'buffer'})");
File.WriteAllBytes("SheetJSJint.xlsb", xlsb.AsUint8Array());
After saving, run the program and pass the test file name as an argument:
dotnet run pres.xlsx
If successful, the program will print the contents of the first sheet as CSV
rows. It will also create SheetJSJint.xlsb
which can be opened in Excel or
another spreadsheet editor.
Running dotnet run
without the filename argument will show an error:
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
at Program.<Main>$(String[] args) in C:\Users\Me\SheetJSJint\Program.cs:line 13
The command must be run with an argument specifying the name of the workbook:
dotnet run pres.xlsx
If the using Jint;
directive is omitted, the build will fail:
'JsValue' does not contain a definition for 'AsUint8Array' and no accessible extension method 'AsUint8Array' accepting a first argument of type 'JsValue' could be found
Standalone Application
- Find the runtime identifier (RID) for your platform5. The RID values for tested platforms are listed below:
Platform | RID |
---|---|
Intel Mac | osx-x64 |
ARM64 Mac | osx-arm64 |
Windows 10 (x64) | win10-x64 |
Windows 11 (ARM) | win-arm64 |
Linux (x64) | linux-x64 |
Linux (ARM) | linux-arm64 |
- Build the standalone application.
Tested platforms (click to hide)
- Intel Mac
- ARM64 Mac
- Linux x64
- Linux ARM
- Windows x64
- Windows ARM
For Intel Mac, the RID is osx-x64
and the command is
dotnet publish -c Release -r osx-x64 --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=true
For Apple Silicon, the RID is osx-arm64
and the command is
dotnet publish -c Release -r osx-arm64 --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=true
For x64 Linux, the RID is linux-x64
and the command is
dotnet publish -c Release -r linux-x64 --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=true
For x64 Linux, the RID is linux-arm64
and the command is
dotnet publish -c Release -r linux-arm64 --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=true
For Windows 10 x64, the RID is win10-x64
and the command is:
dotnet publish -c Release -r win10-x64 --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=true
For Windows 11 ARM64, the RID is win-arm64
and the command is:
dotnet publish -c Release -r win-arm64 --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=true
- Copy the generated executable to the project directory.
The binary name will be SheetJSJint
or SheetJSJint.exe
depending on OS.
The last line of the output from Step 9 will print the output folder.
Tested platforms (click to hide)
- Intel Mac
- ARM64 Mac
- Linux x64
- Linux ARM
- Windows x64
- Windows ARM
For Intel Mac, the RID is osx-x64
and the command is:
cp bin/Release/net*/osx-x64/publish/SheetJSJint .
For Apple Silicon, the RID is osx-arm64
and the command is:
cp bin/Release/net6.0/osx-arm64/publish/SheetJSJint .
For x64 Linux, the RID is linux-x64
and the command is
cp bin/Release/net*/linux-x64/publish/SheetJSJint .
For x64 Linux, the RID is linux-arm64
and the command is
cp bin/Release/net6.0/linux-arm64/publish/SheetJSJint .
For Windows 10 x64, the RID is win10-x64
and the command is:
copy .\bin\Release\net6.0\win10-x64\publish\SheetJSJint.exe .
In some test runs, the copy
command failed with a clear message:
The system cannot find the path specified.
The correct command was
copy .\bin\x64\Release\net6.0\win10-x64\publish\SheetJSJint.exe .
For Windows 11 ARM64, the RID is win-arm64
and the command is:
copy .\bin\Release\net6.0\win-arm64\publish\SheetJSJint.exe .
- Run the generated command.
- Linux/MacOS
- Windows
./SheetJSJint pres.xlsx
.\SheetJSJint pres.xlsx
Footnotes
-
The Jint project recommends the "MyGet" service. According to the developers, the "NuGet" package is "occasionally published". ↩
-
See "Supported Output Formats" in "Writing Files" for details on
bookType
↩ -
See ".NET RID Catalog" in the .NET documentation ↩