Network Working Group J. Bognar | |
Request for Comments: 9999 | |
Category: Informational Salesforce | |
Feb 2017 | |
***DRAFT*** | |
URI Object Notation (UON): Generic Syntax | |
About this document | |
This memo provides information for the Internet community. It does | |
not specify an Internet standard of any kind. Distribution of this | |
memo is unlimited. | |
Copyright Notice | |
Licensed to the Apache Software Foundation (ASF) under one | |
or more contributor license agreements. See the NOTICE file | |
distributed with this work for additional information | |
regarding copyright ownership. The ASF licenses this file | |
to you under the Apache License, Version 2.0 (the | |
"License"); you may not use this file except in compliance | |
with the License. You may obtain a copy of the License at | |
http://www.apache.org/licenses/LICENSE-2.0 | |
Unless required by applicable law or agreed to in writing, | |
software distributed under the License is distributed on an | |
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
KIND, either express or implied. See the License for the | |
specific language governing permissions and limitations | |
under the License. | |
Abstract | |
This document describes a grammar that builds upon RFC2396 | |
(Uniform Resource Identifiers). Its purpose is to define a | |
generalized object notation for URI query parameter values similar in | |
concept to Javascript Object Notation (RFC4627). The goal is a | |
syntax that allows for array and map structures to be | |
such that any data structure defined in JSON can be losslessly | |
defined in an equivalent URI-based grammar, yet be fully compliant | |
with the RFC2396 specification. | |
This grammar provides the ability to construct the following data | |
structures in URL parameter values: | |
OBJECT | |
ARRAY | |
NUMBER | |
BOOLEAN | |
STRING | |
NULL | |
Example: | |
The following shows a sample object defined in Javascript: | |
var x = { | |
id: 1, | |
name: 'John Smith', | |
uri: 'http://sample/addressBook/person/1', | |
addressBookUri: 'http://sample/addressBook', | |
birthDate: '1946-08-12T00:00:00Z', | |
otherIds: null, | |
addresses: [ | |
{ | |
uri: 'http://sample/addressBook/address/1', | |
personUri: 'http://sample/addressBook/person/1', | |
id: 1, | |
street: '100 Main Street', | |
city: 'Anywhereville', | |
state: 'NY', | |
zip: 12345, | |
isCurrent: true, | |
} | |
] | |
} | |
Using the syntax defined in this document, the equivalent | |
UON notation would be as follows: | |
x=(id=1,name='John+Smith',uri=http://sample/ | |
addressBook/person/1,addressBookUri=http://sample/ | |
addressBook,birthDate=1946-08-12T00:00:00Z,otherIds=null, | |
addresses=@((uri=http://sample/addressBook/ | |
address/1,personUri=http://sample/addressBook/ | |
person/1,id=1,street='100+Main+Street',city= | |
Anywhereville,state=NY,zip=12345,isCurrent=true))) | |
1. Language constraints | |
The grammar syntax is constrained to usage of characters allowed by | |
URI notation: | |
uric = reserved | unreserved | escaped | |
reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | | |
"$" | "," | |
unreserved = alphanum | mark | |
mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" | |
In particular, the URI specification disallows the following | |
characters in unencoded form: | |
unwise = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`" | |
delims = "<" | ">" | "#" | "%" | <"> | |
The exclusion of {} and [] characters eliminates the possibility of | |
using JSON as parameter values. | |
2. Grammar constructs | |
The grammar consists of the following language constructs: | |
Objects - Values consisting of one or more child name/value pairs. | |
Arrays - Values consisting of zero or more child values. | |
Booleans - Values consisting of true/false values. | |
Numbers - Decimal and floating point values. | |
Strings - Everything else. | |
2.1. Objects | |
Objects are values consisting of one or more child name/value pairs. | |
The (...) construct is used to define an object. | |
Example: A simple map with two key/value pairs: | |
a1=(b1=x1,b2=x2) | |
Example: A nested map: | |
a1=(b1=(c1=x1,c2=x2)) | |
2.2. Arrays | |
Arrays are values consisting of zero or more child values. | |
The @(...) construct is used to define an array. | |
Example: An array of two string values: | |
a1=@(x1,x2) | |
Example: A 2-dimensional array: | |
a1=@(@(x1,x2),@(x3,x4)) | |
Example: An array of objects: | |
a1=@((b1=x1,b2=x2),(c1=x1,c2=x2)) | |
2.3. Booleans | |
Booleans are values that can only take on values "true" or "false". | |
Example: Two boolean values: | |
a1=true&a2=false | |
2.4. Numbers | |
Numbers are represented without constructs. | |
Both decimal and float numbers are supported. | |
Example: Two numerical values, one decimal and one float: | |
a1=123&a2=1.23e1 | |
2.5. Null values | |
Nulls are represented by the keyword 'null': | |
a1=null | |
2.6. Strings | |
Strings are encapsulated in single quote (') characters. | |
Example: Simple string values: | |
a1='foobar'&a2='123'&a3='true' | |
The quotes are optional when the string cannot be confused | |
with one of the constructs listed above (i.e. objects/arrays/ | |
booleans/numbers/null). | |
Example: A simple string value, unquoted: | |
a1=foobar | |
Strings must be quoted for the following cases: | |
- The string can be confused with a boolean or number. | |
- The string is empty. | |
- The string contains one or more whitespace characters. | |
- The string starts with one of the following characters: | |
@ ( | |
- The string contains any of the following characters: | |
) , = | |
For example, the string literal "(b1=x)" should be | |
represented as follows: | |
a1='(b1=x)' | |
The tilde character (~) is used for escaping characters to prevent | |
them from being confused with syntax characters. | |
The following characters must be escaped in string literals: | |
' ~ | |
Example: The string "foo'bar~baz" | |
a1='foo~'bar~~baz' | |
2.7. Top-level attribute names | |
Top-level attribute names (e.g. "a1" in "&a1=foobar") are treated | |
as strings but for one exception. The '=' character must be | |
encoded so as not to be confused as a key/value separator. | |
Note that the '=' character must also be escaped per the UON | |
notation. | |
For example, the UON equivalent of {"a=b":"a=b"} constructed as | |
a top-level query parameter string would be as follows: | |
a~%3Db=a~=b | |
Note that the '=' character is encoded in the attribute name, | |
but it is not necessary to have it encoded in the attribute value. | |
2.8. URL-encoded characters | |
UON notation allows for any character, even UON grammar | |
characters, to be URL-encoded. | |
The following query strings are fully equivalent in structure: | |
a1=(b1='x1',b2='x2') | |
%61%31=%79%6f%75%20%61%72%65%20%61%20%6e%65%72%64%21 | |
3. BNF | |
The following BNF describes the syntax for top-level URI query | |
parameter values (e.g. ?<attrname>=<value>). | |
attrname = (string | null) | |
value = (var | string | null) | |
string = ("'" litchar* "'") | litchar* | |
null = "null" | |
var = ovar | avar | nvar | boolean | number | |
ovar = "(" [pairs] ")" | |
avar = "@(" [values] ")" | |
pairs = pair ["," pairs] | |
pair = key "=" value | |
values = value ["," values] | |
key = (string | null) | |
boolean = "true" | "false" | |
escape_seq = "~" escaped | |
encode_seq = "%" digithex digithex | |
number = [-] (decimal | float) [exp] | |
decimal = "0" | (digit19 digit*) | |
float = decimal "." digit+ | |
exp = "e" [("+" | "-")] digit+ | |
litchar = unencoded | encode_seq | escape_seq | |
escaped = "@" | "," | "(" | ")" | "~" | "=" | |
unencoded = alpha | digit | | |
";" | "/" | "?" | ":" | "@" | | |
"-" | "_" | "." | "!" | "*" | "'" | |
alpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | | |
"j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | | |
"s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | | |
"A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | | |
"J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | | |
"S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | |
digit = "0" | digit19 | |
digit19 = "1" | "2" | "3" | "4" | "5" | "6" | "7" | | |
"8" | "9" | |
digithex = digit | | |
"A" | "B" | "C" | "D" | "E" | "F" | | |
"a" | "b" | "c" | "d" | "e" | "f" | |