Values and limits
How WKL Office JSON turns JSON values into VBA values and back, and where it stops. JSON (RFC 8259) knows
strings, numbers, true/false, null, objects and arrays; VBA knows typed Variant values. Every
crossing between the two is listed here, together with what is lost and what is refused. The member pages of the API reference refer to this page.
From JSON to VBA
| JSON | Value (natural type) | Typed getters |
|---|---|---|
| string | String, unchanged | GetString unchanged; GetDate reads ISO 8601 (see Dates); GetBoolean reads “true”/“false” (any case, spaces around it); GetLong and the other number getters read the text as a number |
number without fraction or exponent, in the Long range | Long | every number getter; GetString returns the text as written |
number without fraction or exponent, outside Long | Decimal | GetDecimal |
| number with fraction or exponent | Double | GetDecimal, GetCurrency for exact values (see Numbers) |
true / false | Boolean | GetBoolean; GetString returns “true”/“false” |
null | Null | a getter without default raises an error, a getter with default returns the default; StringValue returns an empty string |
| object, array | not a value: Value raises jeInvalidOperation | GetString / StringValue return compact JSON text |
A default of a typed getter is converted to the getter's type with the conversion rules of VBA
(VariantChangeType); a default that does not convert (for example GetLong(path, "abc")) raises
jeTypeMismatch. GetValue returns its default unchanged.
Typed getters: accepted, lossy, refused
| Getter | Accepts | Lossy, without an error | Refused with jeTypeMismatch |
|---|---|---|---|
GetLong | numbers and number strings with a whole value (12.0, 1e3) | - | a fraction; a value outside -2,147,483,648 to 2,147,483,647 |
GetDouble | numbers, number strings | rounded to the nearest Double (15-17 significant digits: 9007199254740993 reads as 9007199254740992); magnitudes below about 5E-324 become 0 | a value beyond ±1.8E308 (1e400) |
GetCurrency | numbers, number strings | rounded to four decimal places, half to even (as CCur); magnitudes below 1E-28 become 0 | a value outside ±922,337,203,685,477.5807 |
GetDecimal | numbers, number strings | rounded to 28-29 significant digits; magnitudes below 1E-28 become 0 | a value beyond ±79,228,162,514,264,337,593,543,950,335 |
GetDate | ISO 8601 strings (see Dates) | fraction digits beyond the seventh are ignored | numbers (also Unix timestamps), other strings, dates before the year 100 |
GetBoolean | true/false; the strings “true”/“false” (any case, spaces around them); numbers | - | other strings, objects, arrays |
GetString | every value; objects and arrays as compact JSON text | - | - |
A string is read as a number only when its text without surrounding spaces is a JSON number: "42" and " 4.2 " are
read; "007", "+5", ".5", "12,5" and "1 000" raise jeTypeMismatch - read such codes with GetString. For
GetBoolean, a number is False when it is 0 after the conversion to Decimal; this includes numbers below 1E-28 such
as 1e-30 (unlike CBool), and every other number, also one beyond the Decimal range, is True.
From VBA to JSON
What SetValue, Add, Insert and CreateValue accept (Value = … of a value node takes only the values without
children of this table, or a JValue; a node with children, an array or a JDoc with an object or array as root
raises jeInvalidOperation there, and Value = … on an object or array always raises jeInvalidOperation):
| VBA value | JSON |
|---|---|
String | string |
Byte, Integer, Long, LongLong | number |
Single | number, written in the shortest text of the Single (0.1! becomes 0.1); NaN and infinity raise jeUnsupportedValue |
Double | number; NaN and infinity have no JSON form and raise jeUnsupportedValue |
Currency | number, exact, without trailing zeros (CCur(12.50) is written 12.5) |
Decimal | number, exact, with its scale (CDec("12.50") is written 12.50) |
Boolean | true / false |
Date | string in ISO 8601 (see Dates) |
Empty, Null, Nothing | null |
a node (JNode, JElement, JArray, JValue) | the node; a node of another document is copied |
a JDoc | a copy of its root; an empty JDoc gives null |
| a one-dimensional array | array, element by element with these rules; a Byte() array becomes an array of numbers (not Base64), an array without dimensions becomes [] |
an array with more dimensions (for example from GetRows), any other object, Error, a missing optional argument | refused with jeUnsupportedValue |
Numbers
A number keeps the text it was read with; Json, Save and GetString write that text unchanged, also
0.10, 1E3 or a number with 40 digits. Only the conversion to a VBA type can round or fail:
GetDecimalandGetCurrencyconvert the text directly, not throughDouble, so0.1 + 0.2stays exact.Decimalholds 28 to 29 significant digits: a number with more digits is rounded to that precision without an error, and a number whose magnitude is below 1E-28 (for example1e-30) becomes 0 without an error; read such values withGetDouble.Currencyrounds to four decimal places.ValuereturnsLongorDecimalfor whole numbers; a whole number beyond theDecimalrange (about 7.9 × 10^28) is returned asDouble, with the precision of aDouble.- A number beyond the
Doublerange (1e400) is valid JSON; reading it withValueorGetDoubleraisesjeTypeMismatch;GetStringreturns its text.
Dates
JSON has no date type; dates travel as strings.
Reading (GetDate, DateValue) accepts the forms that web services send (RFC 3339 profile of ISO 8601):
2026-09-25, 2026-09-25T14:30, 2026-09-25T14:30:15, a fraction of a second with a point or a comma (up
to seven digits are read, more are ignored), each time optionally followed by Z, +02:00, +0200 or +02.
A space instead of T is accepted. Refused: week and ordinal dates, durations, second 60, 24:00, and a date
without time that carries a zone. A date before the year 100, the first date VBA knows, raises jeTypeMismatch. DateZone of the document decides what a timestamp with zone becomes:
converted to local time (default), to UTC, or as written (see
Dates and time zones). Local time uses the time-zone
rules Windows has for the date.
Writing gives yyyy-MM-dd for a date at midnight, otherwise yyyy-MM-ddTHH:mm:ss, with .fff when the value
has milliseconds, always without zone. A VBA time without date (#14:30#) is written with the VBA base date,
1899-12-30T14:30:00.
Documents
| Subject | Behavior |
|---|---|
| Syntax | Strict RFC 8259: comments, trailing commas, single quotes, NaN and leading zeros are errors with line and column |
| Nesting | at most 512 levels; deeper makes LoadJson/Load return False with ParseError.ErrorCode = jeParseError. From 2.1.0 the limit also applies to documents built in code and to VBA arrays of arrays: an insertion beyond it raises jeInvalidOperation |
| Duplicate keys | allowed; the last value wins, the key keeps the position of its first occurrence |
| Size | from 2.1.0 at most MaxInputSize bytes (default 256 MB, 0 = no limit): for a larger input LoadJson/Load return False with ParseError.ErrorCode = jeTooLarge; Load does not read a larger file at all. LoadJson measures the text in UTF-8 bytes. Without a limit, memory decides |
| Unicode | any text; a lone surrogate (\ud800) is read and written again as \u escape |
Load | UTF-8 without byte order mark, or UTF-8, UTF-16 or UTF-32 with one. Without a byte order mark, invalid UTF-8 makes Load return False with ParseError.ErrorCode = jeFileError; with a byte order mark, invalid bytes are replaced by U+FFFD and Load succeeds |
Save | UTF-8 without byte order mark |
DateZone | a value outside the enumeration raises jeInvalidOperation |
JPath
JPath implements the syntax and semantics of RFC 9535 (JSONPath). Where it differs:
- Extensions. A path without
$starts at the current node (also@at the top level);-is allowed in short names ($.first-name); whitespace around the whole expression is ignored; a regular expression stops after one second and then yields false. - Accepted although RFC 9535 requires an error. Function expressions that are not well-typed (
$[?length(@.*) < 3],$[?length(@.a)],$[?match(@.a,'x') == true]),!directly before a comparison,\'inside a double-quoted string, the indices-0and01, and indices beyond ±(2^53−1). - Different results. Strings are ordered by UTF-16 code units, not by Unicode scalar values (this differs only between characters U+E000–U+FFFF and characters above U+FFFF). Numbers are compared as
Decimal: numbers below 1E-28 equal 0, and numbers that differ only after the 28th significant digit are equal. A function of typeValueTypeused as a test is false for the valuefalse. - Regular expressions.
matchandsearchuse .NET regular expressions instead of I-Regexp (RFC 9485):.matches one UTF-16 code unit (an emoji needs two) and also CR;\d,\w,\s, back-references and look-around are accepted. An invalid pattern yields false, as RFC 9535 section 2.4.6 requires. From 2.1.0 a pattern without back-references and look-around runs on an engine whose time grows only linearly with the text; a pattern with these constructs runs as before, with the one-second time limit. - Nested expressions. From 2.1.0 an expression with more than 256 levels of parentheses, negations, function arguments and filters within filters (a filter within a filter counts twice) raises
jePathSyntax. - Paths.
Pathof a node returns this library's expression in dot notation where a name allows it, otherwise['name']($.lines[2].no,$['first name']), not the bracket form of a Normalized Path (RFC 9535, section 2.7).
Compiled expressions are kept in a cache of 512 entries.
Threading and versions
The objects are COM objects of the apartment model: create and use them in one thread, as VBA does; they are not thread-safe for other hosts.
Supported configuration: Microsoft 365 Access 16.0, 32-bit and 64-bit (ACCDB, ACCDE and the Access Runtime) on Windows 11. Other Access versions (2016, 2019, 2021, LTSC) and other Windows versions are not supported in this release.
Standards
| Source | Used for | Deviation |
|---|---|---|
| RFC 8259, JSON (Internet Standard, 2017) | Syntax accepted by LoadJson/Load and written by Json/Save | Duplicate names: the RFC leaves the behavior open (names SHOULD be unique); the last value wins here |
| RFC 9535, JSONPath (Proposed Standard, 2024) | JPath expressions | extensions, lenient acceptance, string order, number comparison, regular expressions and Path: see JPath |
| RFC 9485, I-Regexp (Proposed Standard, 2023) | The regex flavor RFC 9535 prescribes | not implemented (see above) |
| RFC 3339, date and time (2002), section 5.6 | Timestamps read by GetDate | also accepted: a space instead of T, offsets +0200/+02, a comma as fraction separator |
| MSXML `loadXML`) | Model of LoadJson: returns False on invalid text, the reason in ParseError | none |