npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@stenway/oml

v1.1.0

Published

Oh My Lord - Yet another JavaScript Object Notation

Downloads

20

Readme

OML

About OML

OML is a format like JSON, but tries to be much more human-friendly by getting rid of commas, allowing comments, and reducing the amount of double-quotes required. Like with JSON, you have objects, arrays, strings, numbers, booleans and null, but the syntax is a bit different. See a comparison:

JSON:

{
	"key1": true,
	"key2": "Hello world",
	"key3": [10, 20.5, null, false],
	"key4": {
		"sub1": "Test"
	}
}

OML:

{
	key1 = true
	key2 = "Hello world"
	key3 = [10 20.5 null false]
	key4 = {
		sub1 = Test
	}
	# this is a comment
}

You can see there are no commas and instead of a colon character the equal sign character is used to notate a key-value-pair. Their is also a line with a comment starting with the hash character. You can also see the string value "Test" is not put inside of double-quotes. That's a big difference, because OML only needs the double-quotes when the string value contains:

  • Unicode whitespace
  • one of the following characters: " # ' = [ ] { }

equals one of the following keywords:

  • true, false, null

is empty or equals a number.

{
	"with whitespace" = "Hello world"
	"special characters" = ["""" "#" "'" "=" "[" "]" "{" "}"]
	"like keywords" = ["true" "false" "null"]
	empty = ""
	"like a number" = ["0" "-10" "2.5" "10.3E2"]
}

OML strings don't have C-like escape sequences like JSON does. Instead their is only an escape sequence for the double-quote and the line-feed character (like WSV and SML does it: https://www.whitespacesv.com / https://www.simpleml.com / https://www.npmjs.com/package/@stenway/wsv / https://www.npmjs.com/package/@stenway/sml) and for every else character just use the Unicode character itself. This is because OML tries to follow the rule: "Do it one way - don't offer multiple ways".

JSON:

["Hello \"World\"", "Line1\nLine2", "\u00A5"]

OML:

["Hello ""World""" "Line1"/"Line2" ¥]

OML is case-sensitive like JSON. A difference to JSON on the other hand is, that OML differentiates numbers as integer and floats:

{
	integers = [0 +10 123 -10000]
	floats = [0.0 +10.0 1.234 10.2E-4 0.23e8]
}

Another difference to JSON is, that OML also has another value type, which is the value type character, where a single character (Unicode scalar value) is put inside of single-quotes:

['a' ' ' '𝄞']

The representation of a single quote character or a line-feed character looks a bit funny, but it's consistent with the way the two string escape sequences work:

['''' ''/'']

When an object has duplicate keys, the behavior is strictly defined. The previous value is simply overwritten:

{
	key = 123
	key = 456
}

Here the resulting value is the number 456.

Simple values like strings, booleans, numbers and null can be the root value. It does not need to be an object or array:

"Simple string as root value"

Comments and whitespaces can be put before and after the root value:

# This is my copyright info
{ 
	# Here is my object data
	key = 12345
}

# -----------
# End of file
# -----------

An OML file is like WSV and SML a ReliableTXT file (https://www.reliabletxt.com / https://www.npmjs.com/package/@stenway/reliabletxt). So when you write a file you need to write the UTF-8 byte order mark (BOM) when using UTF-8 encoding.

See some videos for ReliableTXT here:

Installation

Using NPM:

npm install @stenway/oml

Getting started

The Oml class has like the JSON object two methods: stringify and parse. You can first try out the stringify method like this and play around with the output:

import { Oml } from '@stenway/oml'
console.log(Oml.stringify({key1: 123, key2: [true, false, null, "Hello world"]}))

This will output the following OML string:

{key1=123 key2=[true false null "Hello world"]}

You can make the output prettier by adding formating options. When you pass an empty object, the default formatting options apply:

const obj = {key1: 123, key2: [true, false, null, "Hello world"]}
console.log(Oml.stringify(obj, {}))

And you get this:

{
	key1 = 123
	key2 = [true false null "Hello world"]
}

To parse an OML string, use the parse method:

const str = `{test="Hello world" test2=null}`
console.log(Oml.parse(str))

Replacing

The stringify method has a replacer functionality:

const obj = {
	test1: new Date(2021, 1, 4, 12, 40),
	test2: undefined
}
console.log(Oml.stringify(obj, {}, (root, parent, key, value) => {
	if (value === undefined) { return "<Undefined>" }
	if (value instanceof Date) { return value.toISOString() }
	return value
}))

Which will give you:

{
	test1 = 2021-02-04T11:40:00.000Z
	test2 = <Undefined>
}

OML does not allow the input to be NaN, +Infinity, -Infinity or undefined and will throw an error. With the replacer functionality you can avoid the thrown error by checking for such cases.

You can also react, when a circular reference was detected:

const obj = {
	child: {}
}
const obj2 = {
	child: obj
}
obj.child = obj2
console.log(Oml.stringify(obj, null, (root, parent, key, value, cyclic) => {
	if (cyclic) { return "<CircularReference>" }
	return value
}))

Which will give you:

{child={child=<CircularReference>}}

If your object/class has a toOml method, you can control how the object is serialized:

class TestClass1 {
	toOml(): string {
		return "Test Class 1"
	}
}

console.log(Oml.stringify(new TestClass1()))

Which will give you:

"Test Class 1"

Reviving

The parse method on the other hand has a reviver functionality:

const content = `{test="@BigInt:123" test2="Str"}`
const result = Oml.parse(content, (owner, key, value) => {
	if (typeof value === 'string' && value.startsWith("@BigInt:")) {
		return BigInt(value.substring(8))
	}
	return value
})

Which will give you:

{test: BigInt(123), test2: "Str"}

You can also get access to the original string before interpretation, to resolve problems with JavaScript precision:

const content = `{tooBig=-9999999999999999 ok=9007199254740991}`
console.log(Oml.parse(content, (owner, key, value, source) => {
	if (typeof value === 'number' && Math.abs(value) > Number.MAX_SAFE_INTEGER) {
		return BigInt(source!)
	}
	return value
}))

Which will give you:

{tooBig: BigInt("-9999999999999999"), ok: 9007199254740991}

Formatting

You can control the formatting with the following properties: indentation, beforeEqual, afterEqual, alignChar, maxLevel, reduceSimpleArray.

For example control the used indentation whitespace sequence. The default indentation character is a tab. Here we change it to use two spaces:

console.log(Oml.stringify({key1: true, key2: "Hello world"}, {indentation: "  "}))

Which will give you:

{
  key1 = true
  key2 = "Hello world"
}

By specifying an empty string as indentation, you can deactivate indentation:

console.log(Oml.stringify({key1: true, key2: "Hello world"}, {indentation: ""}))

Which will give you:

{
key1 = true
key2 = "Hello world"
}

Another example is, that you can control the maximum depth level where formatting should be applied:

console.log(Oml.stringify({matrix: [[1, 0], [0, 1]]}, {maxLevel: 1}))

Which will give you:

{
	matrix = [
		[1 0]
		[0 1]
	]
}

Or align your keys with another whitespace character:

console.log(Oml.stringify(
	{"個人情報": "田中", "日付": "2021-01-02", "エンド": true},
	{alignChar: "\u3000"}
))

Which will give you:

{
	個人情報 = 田中
	日付   = 2021-01-02
	エンド  = true
}

Document class

The OmlDocument class offers easy methods to convert your document to bytes or back from bytes by combining your data with the ReliableTxtEncoding property from the @stenway/reliabletxt package:

const document = new OmlDocument({test: true, test2: "Hello world"}, ReliableTxtEncoding.Utf8)
const omlStr = document.toString()
const bytes = document.getBytes()

const document2 = OmlDocument.fromBytes(bytes)

And of course you can easily convert your document to a Reliable Base64 string and back:

const document = new OmlDocument({test: true, test2: "Hello world"}, ReliableTxtEncoding.Utf8)
const base64Str = document.toBase64String()
console.log(base64Str)

const document2 = OmlDocument.fromBase64String(base64Str)

With the Reliable Base64 string being:

Base64|77u/e3Rlc3Q9dHJ1ZSB0ZXN0Mj0iSGVsbG8gd29ybGQifQ|

File IO

For file reading and writing functionality see the oml-io package.

Comparison to JSON

OML creates in the most common cases smaller files than JSON, because it uses less double-quotes:

const obj = {
	key1: 123,
	key2: true,
	key3: "Hello world",
	key4: null,
	key5: [0, 10, 20],
	key6: {
		width: 1920,
		height: 1080
	}
}

console.log(JSON.stringify(obj))
console.log(Oml.stringify(obj))

Compare the results:

{"key1":123,"key2":true,"key3":"Hello world","key4":null,"key5":[0,10,20],"key6":{"width":1920,"height":1080}}
{key1=123 key2=true key3="Hello world" key4=null key5=[0 10 20] key6={width=1920 height=1080}}