Skip to content

Latest commit

 

History

History
259 lines (171 loc) · 8.61 KB

uri-charge.md

File metadata and controls

259 lines (171 loc) · 8.61 KB

URI Charge Notation

URI Charge Notation designed to encode arbitrary JavaScript values as part of URI.

URI charge represents everything what JSON does, and even more. E.g. it is able to represent BigInt values natively.

The notation is extensible, so it may represent something that needs a special representation. So, the Infinity, negative Infinity and NaN values are representable within URI charge with standard extensions enabled.

URI charge may present in various parts of URI. E.g. within query:

?find=includes(first_name(john))&order=first_name(asc(!))second_name(asc(!))birthday(asc(-))&range=from(10)to(20)

Primitive Values

Any JavaScript primitive, except Symbol and undefined (which stands value absence), has its representation within URI charge.

Number

Numbers encoded as is:

  • Any string started with decimal digit ("0" (U+OO30) - "9" (U+OO39)) treated as positive number.
  • Any string starting with hyphen ("-" (U+002D)) followed by decimal digit treated as negative number.

The number string can be percent-encoded.

Negative zero encoded as -0.

Hexadecimal and binary formats are also supported. For that, the string should start with 0x/-0x and 0b/-0b respectively.

Decimal digits also supported: 3.14159265359, 0.1E-23. Note that the leading 0 can not be omitted.

BigInt

BigInt values encoded as decimal string preceded by 0n or -0n prefix:

?from=-0n12344543&to=0n4354354452354

Boolean

  • true encoded as ! - exactly one exclamation mark ("!" (U+0021)).
  • false encoded as - - exactly one hyphen ("-" (U+002D)).

Null

null encoded as -- - exactly two hyphens ("-" (U+002D)).

String

String represented as percent-encoded value.

Additionally:

  • Since parentheses ("(" (U+0028) and ")" (U+0029)) and comma ("," (U+002C)) have special meaning within URI charge, they should be percent-encoded.
  • When encoded value starts with apostrophe ("'" (U+0027)), the apostrophe is stripped, and the actual string value starts from the second symbol.
  • A quoted string starting with apostrophe may include balanced set of parentheses ("(" (U+0028) and ")" (U+0029)). I.e. each closing parenthesis should match the opening one preceding it. A comma ("," (U+002C)) is considered a part of such string only if it is enclosed into parentheses. This may be used to place unchanged URI charge as a string value.
  • Since decimal digits, "!" (U+0021), "$" (U+0024), "'" (U+0027), and "-" (U+002D) prefixes have special meaning, they should be escaped with apostrophe ("'" (U+0027)).

Empty string may be left as is or encoded as single apostrophe ("'" (U+0027)).

?first=John&middle='&last=Doe&birthday='1970-01-01

Raw Value

Raw value is the one not yet processed as numeric value, null (--), or false (-).

Data schema may change the way the raw value parsed. E.g. it may wish to always treat it as string.

This, however, does not affect quoted strings, or values starting with "!" (U+0021) or "$" (U+0024).

List

List corresponds to JavaScript array literal.

List encoded as series of item values separated by comma ("," (U+002C)).

foo,bar,baz

represents an array like

["foo", "bar", "baz"]

Leading and trailing comma ignored within list. So the list above can be encoded as:

,foo,bar,baz
foo,bar,baz,
,foo,bar,baz,

Empty list encoded as single comma ("," (U+002C)). This is possible, because such comma is ignored.

List with single item has to contain at leas one (leading or trailing comma to distinguish it from single value:

,foo
foo,
,foo,

An item value is encoded within URI charge. Thus, it can be anything:

  • boolean value: !,-
  • number: -128,127
  • empty string: ,' or ,,

Nested List

An list item containing another (nested) list should be enclosed into parentheses:

(foo,bar),(baz)

represents an array like

[["foo", "bar"], ["baz"]]

Note that comma is completely optional after nested list. So the list above can be encoded as:

(foo,bar)(baz)

Any level of nesting supported:

(1,(2.1,(2.1.1,2.1.2))((3.1.1,3.1.2)4.1)5)

Map

Map corresponds to JavaScript object literal.

Map encoded as series of key/value entries. Each entry encoded as key followed by value enclosed into parentheses.

column(first_name)includes(john)

represents an object like

{
  "column": "first_name",
  "includes": "john"
}

Empty map has special representation: $

An entry value is encoded within URI charge. Thus, it can be anything:

  • boolean value: foo(!)bar(-)
  • number: from(-128)to(127)
  • null: is-null(--)
  • nested map: foo(bar(baz))
  • nested empty map: foo($)
  • list: foo(bar,baz)
  • empty list: foo(,)
  • multidimensional list: `foo((item1.1,item1.2)(item2.1,item2.2))
  • empty string: foo()

Map entry may be specified multiple times. However, it is up to parser (or data schema) how to interpret this. E.g. multiple values may be treated as list items. By default, the last entry value overrides preceding ones.

The following rules apply to entry keys:

  • Since parentheses ("(" (U+0028) and ")" (U+0029)) and comma ("," (U+002C)) have special meaning within URI charge, they should be percent-encoded.
  • When encoded value starts with dollar sign ("$" (U+0024)), the dollar sign is stripped, and the actual key value starts from the second symbol. This can be used to escape symbols that have special meaning, except parentheses, that should be percent-encoded.
  • Since "!" (U+0021), "$" (U+0024), and "'" (U+0027) prefixes have special meaning, they should be escaped with dollar sign ("$" (U+0024)).
  • Empty key represented by single dollar sign ("$" (U+0024)).

A special case when key prefixed with dollar sign is not followed by value is treated as entry with empty string value. I.e. $key is the same as $key(). Note that this rule does not work for single $ symbol, which stands for empty object. The $() has to be used for object with empty key and empty value ({ '': '' }).

A map may have suffix. I.e. the last entry key without value. Such suffix is treated as entry with empty string value. So, foo(bar)suffix is the same as foo(bar)suffix() or foo(bar)suffix(')).

Entities

URI Charge Notation can be extended with custom entities. An entity is an opaque syntax construct that don't have special meaning, unless recognized by custom handler.

Entity starts with exclamation mark ("!" (U+0021)) followed by entity name. Entity name should not include apostrophe ("'" (U+0027)) or other delimiters.

The following entities supported by standard "Non-Finite Numbers" extension:

  • !Infinity is treated as Infinity (positive infinity) numeric value.
  • !-Infinity is treated as -Infinity (negative infinity) numeric value.
  • !NaN is treated as NaN (not-a-number) value.

Formatted Data

Formatted data extends URI Charge Notation with additional data formats, such as base64.

Formatted data starts with exclamation mark ("!" (U+0021)) followed by format name, apostrophe ("'" (U+0027)), and arbitrary data in the named format. The data may include e.g. balanced set of parentheses, just like a quoted string.

Example formatted data:

!base64'SGVsbG8sIFdvcmxkIQ

Metadata

Metadata can be attached to any value. For that, any number of metadata attributes may precede the value.

Metadata attribute starts with exclamation mark ("!" (U+0021)) followed by attribute name and value enclosed into parentheses.

Unlike entities and formatted data, metadata attribute don't have to be recognized.

Charge processor may use metadata e.g. as data processing parameter:

content-type(text,plain)charset(utf-8)base64'SGVsbG8sIFdvcmxkIQ