Skip to content

Commit 283de32

Browse files
authored
feat: JSON from and to JUON (JS URL Object Notation) (#61)
* feat: JUON parsing * feat: numbers with exponent, object tests * chore: add readme for JUON and more tests * chore: improved README text * feat: JUON builder * fix: JUON <=> JSON should not change quoting content * fix: JsonPath with dot always needs curly escape
1 parent 9bdbae1 commit 283de32

File tree

8 files changed

+1079
-2
lines changed

8 files changed

+1079
-2
lines changed

JUON.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# JUON
2+
3+
JUON is short for JS URL Object Notation.
4+
Think of it as JSON values for URLs.
5+
That means the syntax does not use characters that are not safe to use in URLs.
6+
7+
It is not only inspired by JSON but designed to be easily
8+
translatable to JSON. For that, it shares the same types:
9+
_object_, _array_, _string_, _number_, _boolean_ and _null_.
10+
11+
Where possible the JSON syntax was kept in JUON.
12+
This should help both translate but also remember the syntax for everyone
13+
already familiar with JSON.
14+
15+
The key syntax differences are:
16+
17+
* object and arrays both use round brackets `(...)`
18+
* strings are quoted in single upticks `'...'`
19+
* positive numbers or positive exponents must not use `+` (omit it)
20+
* object member names are not quoted
21+
* object member names can only use `A-Z a-z 0-9 - . _ @`
22+
* object member names must start with a letter or `@`
23+
* empty array is `()`, empty object cannot be expressed (approximate with `null`)
24+
* as strings are quoted with single upticks `"` does not need escaping
25+
but instead `'` is escaped as `\'`
26+
27+
Furthermore, JUON is more "lenient" than JSON both to be more user-friendly and
28+
to allow shorter URLs though the use of shorthands and omissions:
29+
30+
* `null` can be encoded as the empty string or shortened to `n`
31+
* `true` can be shortened to `t`
32+
* `false` can be shortened to `f`
33+
* numbers can start with `.` (= `0.`)
34+
* numbers can end with `.` (= `.0`)
35+
36+
In summary, JUON can express everything JSON can, except the empty object `{}`.
37+
Some characters in strings obviously will need URL encoding to be allowed in URLs.
38+
39+
## Example
40+
Here is a JSON example:
41+
```json
42+
{
43+
"name": "Freddy",
44+
"age": 30,
45+
"car": null,
46+
"addresses": [{
47+
"street": "Elm Street",
48+
"zip": 1428,
49+
"city": "Springwood",
50+
"invoice": true
51+
}]
52+
}
53+
```
54+
In JUON the equivalent Java `String` would be (formatting whitespace removed):
55+
```
56+
(name:'Freddy',age:30,car:null,addresses:((street:'Elm Street',zip:1428,city:'Springwood',invoice:true)))
57+
```
58+
This could be further shortened by using shorthands and omitting `null` values:
59+
```
60+
(name:'Freddy',age:30,car:,addresses:((street:'Elm Street',zip:1428,city:'Springwood',invoice:t)))
61+
```
62+
In a URL parameter the entire value would be URL encoded, resulting in:
63+
```
64+
(name:'Freddy',age:30,car:,addresses:((street:'Elm+Street',zip:1428,city:'Springwood',invoice:t)))
65+
```
66+
(Note: the `+` could also be `%20`)
67+
68+
69+
## Specification
70+

src/main/java/org/hisp/dhis/jsontree/JsonNode.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import java.nio.file.Files;
3939
import java.nio.file.Path;
4040
import java.util.Iterator;
41-
import java.util.List;
4241
import java.util.Map.Entry;
4342
import java.util.Optional;
4443
import java.util.Set;
@@ -128,6 +127,20 @@ static JsonNode ofNonStandard( String json ) {
128127
return JsonTree.ofNonStandard( json ).get( JsonPath.ROOT );
129128
}
130129

130+
/**
131+
* Creates a new lazily parsed {@link JsonNode} tree from special URL object notation.
132+
* <p>
133+
* Note that the {@link JsonNode}'s {@link JsonNode#getDeclaration()} will be the equivalent JSON, not the original
134+
* URL notation.
135+
*
136+
* @param juon a value in URL notation
137+
* @return the given URL notation input as {@link JsonNode}
138+
* @since 1.3
139+
*/
140+
static JsonNode ofUrlObjectNotation(String juon) {
141+
return of( Juon.toJson( juon ));
142+
}
143+
131144
/**
132145
* Create a new lazily parsed {@link JsonNode} tree.
133146
*

src/main/java/org/hisp/dhis/jsontree/JsonPath.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ private static String keyOf( String name, boolean forceSegment ) {
9696
// edge special case: {...} but only opens at the start => dot works
9797
if ( !hasDot && name.charAt( 0 ) == '{' && name.indexOf( '{', 1 ) < 0 ) return "."+name;
9898
// special case: has curly open but no valid curly close => plain or dot works
99-
if (indexOfInnerCurlySegmentEnd( name ) < 1) return name.charAt( 0 ) == '{' ? "."+name : name;
99+
if (!hasDot && indexOfInnerCurlySegmentEnd( name ) < 1) return name.charAt( 0 ) == '{' ? "."+name : name;
100100
return curlyEscapeWithCheck( name );
101101
}
102102

0 commit comments

Comments
 (0)