The string returned from the serialize
function is literal and valid JavaScript, which can be saved to a .js
or .ts
file, or even embedded inside of a <script>
tag.
import { serialize } from "https://deno.land/x/[email protected]/mod.ts";
import { serialize } from "https://x.nest.land/[email protected]/mod.ts";
serialize({
strings: "string",
booleans: true,
numeric: 0,
bigint1: 10n,
bigint2: BigInt(10),
regexp1: /([^\s]+)/g,
regexp2: new RegExp("([^\\s]+)", "g"),
objects: { foo: "foo" },
arrayLikes: [1, 2, 3],
actualNull: null,
undefineds: undefined,
nonFinites: Number.POSITIVE_INFINITY,
dateObject: new Date("2022-11-26T05:48:02.806Z"),
mapObjects: new Map([["hello", "world"]]),
setObjects: new Set([123, 456]),
arrowFuncs: (arg) => `"${arg}"`,
globalSyms: Symbol.for("Deno.customInspect"),
});
The above will produce the following string output:
'{"strings":"string","booleans":true,"numeric":0,"bigint1":BigInt("10"),"bigint2":BigInt("10"),"regexp1":new RegExp("([^\\\\s]+)", "g"),"regexp2":new RegExp("([^\\\\s]+)", "g"),"objects":{"foo":"foo"},"arrayLikes":[1,2,3],"actualNull":null,"undefineds":undefined,"nonFinites":Infinity,"dateObject":new Date("2022-11-26T05:48:02.806Z"),"mapObjects":new Map([["hello","world"]]),"setObjects":new Set([123,456]),"arrowFuncs":(arg)=>`"${arg}"`,"globalSyms":Symbol.for("Deno.customInspect")}';
Note: to produced a beautified string, you can pass an optional second argument to
serialize()
to define the number of spaces to be used for the indentation.
-
Array
-
Date
-
Map
-
Set
-
URL
-
Infinity
-
undefined
-
RegExp
Β·options.literalRegExp
for literal output -
BigInt
Β·options.literalBigInt
for literal output -
Function
Β·options.includeFunction
(enabled by default) -
Getters
Β·options.includeGetters
required -
Symbol
Β·options.includeSymbols
required π§
π§ More information on Symbols and Serialization
The serialize()
function accepts an options
object for a second argument, allowing fine-grained control of various aspects of the program's behavior.
TypeScript interface
interface SerializeOptions {
/**
* Serialize Arrays using the `Array.from` method, which may not be available
* in all target environments (looking at you, Internet Explorer). Default
* behavior is to use the `Array.prototype.splice` method, constructing an
* Array from an Array-like object.
* @default true
*/
arrayFrom?: boolean;
/**
* Enable serializing of any functions encountered in the target object.
* @default true
*/
includeFunction?: boolean;
/**
* Serialize get property accessors ("getters") into their resolved values.
* @default false
*/
includeGetters?: boolean;
/**
* Serialize all properties, including any that are non-enumerable (hidden).
* By default this is disabled, meaning that only enumerable properties are
* serialized and included in the output.
* @default false
*/
includeHidden?: boolean;
/**
* Include `Symbol` primitives (typeof `symbol`) in the serialized output.
* @default true
*/
includeSymbols?: boolean;
/**
* Skips the replacement step, treating the input as pure JSON. This means
* much faster processing times, at the cost of **no support** for objects
* like RegExp/URL/Function/etc. `**You should probably leave this alone.**
* @default false
*/
isJSON?: boolean;
/**
* Serialize BigInt as literals.
* @example BigInt("100") -> 100n
* @default false
*/
literalBigInt?: boolean;
/**
* Serialize RegExp as literals.
* @example RegExp('(a|b|c)', 'g') -> /(a|b|c)/g
* @default false
*/
literalRegExp?: boolean;
/**
* Attempt to silently handle raised exceptions, rather than throwing hard
* exceptions, by skipping over problem areas wherever possible. This is
* ignored if a fatal error is thrown.
* @default true
*/
silent?: boolean;
/**
* Control the indentation width in the generated string.
* Set to 0 to disable pretty-printing entirely.
* @default 0
*/
space?: string | number;
/**
* Sort entries of keyed collections (Array, Object, Set, Map).
* @default false
*/
sorted?: boolean;
/**
* Custom comparator to sort entries (implies {@linkcode sorted} is `true`).
* **Note**: if {@linkcode sorted} is `false`, but this option is defined
* with a valid comparator function, it will override the former and sort
* all entries.
* @default undefined
*/
sortCompareFn?: ((a: unknown, b: unknown) => number) | null;
/**
* The maximum length of a string before it is truncated with an ellipsis.
* @default undefined (no limit)
*/
strAbbreviateSize?: number;
/**
* Skips sanitization of unsafe HTML characters.
* @default false
*/
unsafe?: boolean;
}
Serialize Arrays using the Array.from
method, which may not be available in all target environments.
Default behavior is to use the Array.prototype.splice
method, constructing an Array from an Array-like object.
Default value is
false
This option indicates the target object does not contain any functions or RegExp expressions. This enables a hot-path that allows serialization to be over 3x faster. If you're serializing a lot of data, and know its pure JSON, then you can enable this option for a speed-up.
Default value is
false
serialize(obj, { isJSON: true });
Note: still escaped for XSS prevention (see
options.unsafe
)
Disable this option to ignore JavaScript/TypeScript functions, treating them just like JSON.stringify
(e.g. ignore them entirely). Other features will work as expected.
Default value is
true
serialize(obj, { ignoreFunction: true });
Enable to include the source for getter
accessor properties, which allow for computed values. If not enabled, their values will be calculated at the time of serialization
and fixed to whatever the static output was at that point in time.
Default value is
false
const obj = {
name: "Nick",
title: "dev",
get greet() {
return `Hello, I'm a ${this.title} named ${this.name}!`;
},
};
serialize(obj);
// {"name":"Nick","title":"dev","greet":"Hello, I'm a dev named Nick!"}
serialize(obj, { includeGetters: true });
// {
// "name": "Nick",
// "title": "dev",
// get greet () {
// return `Hello, I'm a ${this.title} named ${this.name}!`;
// }
// }
options.includeHidden
Serialize non-enumerable properties that are normally hidden.
Note: only partially implemented
Default value is
false
β οΈ support for symbols is experimental
Serialize values with the type of symbol
. Only works with global symbols created with the Symbol.for()
method. Any standard symbols will be coerced into globals,
carrying over their description text (if any).
Serializing the symbol primitive is a totally bonkers concept, and - by design - is impossible to truly achieve. In reality, it's just copying the string key from a given symbol, and therefore there is a strong chance the one created during the deserializing process will be a different value entirely.
Default value is
true
Serialize BigInt expressions into literals, e.g. 100n
rather than the default BigInt("100")
.
serialize({ big: BigInt("100") }, { literalBigInt: true });
// '{"big":100n}'
Default value is
false
, with behavior as seen below.
serialize({ bigger: 200n });
// '{"bigger":BigInt("200")}'
Serialize Regular Expressions into literals, e.g. /.../i
rather than the default RegExp("...", "i")
.
serialize({ pattern: /(foo|bar|baz)/i }, { literalRegExp: true });
// '{"pattern":/(foo|bar|baz)/i}'
Default value is
false
, with behavior as seen below.
serialize({ pattern: /(foo|bar|baz)/i });
// '{"pattern":new RegExp("(foo|bar|baz)","i")}'
Attempt to silence most errors and diagnostic messages.
Default value is
true
Sort entries of keyed collections (Array, Object, Set, Map).
Default value is
false
Custom comparator to sort entries in Arrays, Maps, Sets, etc.
Note: Implies options.sorted
. If sorted
is false
while this option is defined with a valid
comparator function, it will override the former.
Default value is
undefined
This option is the same as the space
argument that can be passed to JSON.stringify
. It can be used to add whitespace and indentation to the serialized output to make it more readable.
Default value is
0
serialize(obj, { space: 2 });
Note: not yet implemented
The maximum length of a string before it is truncated with an ellipsis.
Default value is
undefined
(no limit)
This option is to signal serialize()
that we want to do a straight conversion, without the XSS protection. This option needs to be explicitly set to true
. HTML characters and JavaScript line terminators will not be escaped. You will have to roll your own.
Default value is
false
serialize(obj, { unsafe: true });
One of the primary features of this module is to serialize code to a string of literal JavaScript, which can be safely embedded in an HTML document as the content of a <script>
element.
In order to make this safe, HTML entities and JS line terminators are auto-escaped:
const dangerous = { haxorXSS: "</script>" };
The above will produce the following string, HTML-escaped output which is safe to put into an HTML document as it will not cause the inline script element to terminate:
serialize(dangerous);
// HTML entities have been safely escaped:
'{"haxorXSS":"\\u003C\\u002Fscript\\u003E"}';
You can pass unsafe
argument to serialize()
for straight serialization:
serialize(dangerous, { unsafe: true });
// HTML entities have NOT been escaped:
'{"haxorXSS":"</script>"}';
For some use cases you might also need to deserialize the string.
This is explicitly not part of this module. However, you can easily write it yourself:
This is extremely unsafe, should probably be avoided, and is quite possibly unsupported by your environment. To understand why these risks exist, take a look at the code behind the deserialize function:
function deserialize<T>(serialized: string): T {
return eval(`(${serialized})`) as T;
}
Warning: Don't forget to wrap the entire serialized string with parentheses, as seen as
(
)
in the example above). Otherwise the opening bracket{
will be considered to be the start of a body.
import {
deserialize,
serialize,
} from "https://deno.land/x/[email protected]/mod.ts";
interface User {
name: string;
age: bigint;
}
// encoded to a plain old string
const encoded = serialize<User>({ name: "Nick", age: 29n });
// ensures the decoded data has a consistent type, and
// instructs our IDE which properties and types to anticipate.
const decoded = deserialize<User>(encoded);
// => { name: string; age: bigint; }
This section assumes you have the GitHub CLI.
Β β οΈ Fixing a bug? Create an issue first!
Unless, of course, you're fixing a bug for which an issue already exists!
This allows the issue to be connected to your Pull Request, creating a permanent record of your contribution to the project. It also makes it easier for maintainers to track project progression.
Creating an issue also ensures you're given proper credit for fixing that bug π
gh repo fork deno911/serialize --clone
git checkout -b fix/typo-in-readme
Don't forget to format, lint, and test your code before committing!
# hack hack hack...
deno fmt
deno lint --unstable
deno test --unstable -A --no-check
Please try keep all changes concise and relevant to each other.
git add .
# all commits should be signed + verified with GPG/SSH
git commit -S -m "fix: typos in README.md"
git push
gh pr create --title "fix: typos in README.md"
Or just open your repo on GitHub.com and follow the prompts.
As a general rule: the smaller the PR, the better. Why?
- Implementing / reverting the effects of a small pull is easy.
- Large pulls can quickly become unmanageable and fragile.
- Large pulls make it quite difficult to identify, isolate, and fix bugs.
Warning: make sure you select the upstream repo for your PR!