docs.sheetjs.com/docz/docs/03-demos/42-engines/05-jint.md
2023-10-28 23:27:06 -04:00

15 KiB

title sidebar_label description pagination_prev pagination_next
Sheets in .NET with Jint C# + Jint Process structured data in C#. Seamlessly integrate spreadsheets into your program by pairing Jint and SheetJS. Handle the most complex Excel files without breaking a sweat. demos/bigdata/index solutions/input

import current from '/version.js'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock';

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.

:::warning Telemetry

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

:::note pass

Most of the integration functions are not documented. This explanation is based on version 3.0.0-beta-2053.

:::

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"));

:::info pass

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.Realm.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

:::note

This demo was tested in the following deployments:

Architecture Jint Version Date
darwin-x64 3.0.0-beta-2051 2023-09-16
darwin-arm 3.0.0-beta-2051 2023-09-26
win10-x64 3.0.0-beta-2053 2023-10-28
win11-arm 3.0.0-beta-2051 2023-09-26
linux-x64 3.0.0-beta-2052 2023-10-11
linux-arm 3.0.0-beta-2051 2023-09-26

:::

Platform Configuration

  1. Set the DOTNET_CLI_TELEMETRY_OPTOUT environment variable to 1.
How to disable telemetry (click to hide)

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.

  1. Install .NET
Installation Notes (click to show)

For macOS x64 and ARM64, install with Homebrew: brew install dotnet@6

For Steam Deck Holo and other Arch Linux x64 distributions, the dotnet-sdk and dotnet-runtime packages should be installed using pacman.

https://dotnet.microsoft.com/en-us/download/dotnet/6.0 is the official source for Windows and ARM64 Linux versions.

  1. Open a new Terminal window in macOS or PowerShell window in Windows.

Base Project

  1. Create a new folder SheetJSJint and a new project using the dotnet tool:
mkdir SheetJSJint
cd SheetJSJint
dotnet new console --framework net6.0
dotnet run
  1. 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.0.0-beta-2053

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

  1. 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-${current}/package/dist/shim.min.js curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js curl -LO https://sheetjs.com/pres.xlsx}

  1. 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 {current}

Read and Write Files

  1. 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.Realm.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.

:::info pass

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

:::

:::caution pass

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

  1. 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
  1. Build the standalone application. Replace $RID with the real value in:
dotnet publish -c Release -r $RID --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=true
Tested platforms (click to hide)

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
  1. 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)

For Intel Mac, the RID is osx-x64 and the command is:

cp bin/Release/net6.0/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/net6.0/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 .

:::caution pass

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 .
  1. Run the generated command.
./SheetJSJint pres.xlsx
.\SheetJSJint pres.xlsx

  1. The Jint project recommends the "MyGet" service. According to the developers, the "NuGet" package is "occasionally published". ↩︎

  2. See read in "Reading Files" ↩︎

  3. See write in "Writing Files" ↩︎

  4. See "Supported Output Formats" in "Writing Files" for details on bookType ↩︎

  5. See ".NET RID Catalog" in the .NET documentation ↩︎