SURF Format
JSON replacement for rich, readable data interchange.
- SURF
- Simple URF
- URF
- Uniform Resource Framework
SURF Serializes Resources
- literals
- objects
- collections
But why SURF?
Isn't JSON simple and easy?
{
"name": "Jane Doe",
"id": "bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832",
"email": "jane_doe@example.com",
"phone": "+12015550123",
"usernames": ["jdoe", "janed"],
"homePage": "http://www.example.com/jdoe/",
"joined": "2016-01-23",
"balance": 0.90,
"charge": 0.30
}
Isn't JSON good enough?
Want a date?
JSON, Wikipedia, retrieved 2017-05-14.As of 2011, there are some de facto standards, e.g., converting from Date to String, but none universally recognized.
JSON Dates in the Wild
"\"\\/Date(1335205592410)\\/\""
"\"\\/Date(1335205592410-0500)\\/\""
"2012-04-23T18:25:43.511Z"
"@1335205592410@"
But these are still all JSON strings, not dates!
How can one distinguish a date string from a string string? And what about local dates? Times? Durations?
Have a Date @
SURF
{
"name": "Jane Doe",
"id": "bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832",
"email": "jane_doe@example.com",
"phone": "+12015550123",
"usernames": ["jdoe", "janed"],
"homePage": "http://www.example.com/jdoe/",
"joined": @2016-01-23,
"balance": 0.90,
"charge": 0.30
}
SURF Likes Dates!
- Instant:
@2017-02-12T23:29:18.829Z
- ZonedDateTime:
@2017-02-12T15:29:18.829-08:00[America/Los_Angeles]
- LocalDate:
@2017-02-12
- LocaleTime:
@15:29:18.829
- Year:
@2017
Aren't JSON numbers OK?
Calculating a JSON balance…
{"balance": 0.90, "charge": 0.30}
…using Java
double balance = 0.90; //starting account balance of $0.90
double charge = 0.30; //monthly charge of $0.30
while(balance > 0) {
balance -= charge; //subtract the charge each month
System.out.println("balance: " + balance);
}
balance: 0.6000000000000001 balance: 0.3000000000000001 balance: 1.1102230246251565E-16 balance: -0.2999999999999999
Never use IEEE 754 floating point numbers to represent money!
But that's all JSON has!
Should you put money in JSON strings instead?
Should you store integer cents as JSON numbers?
SURF Has a $
Decimal Type
{
"name": "Jane Doe",
"id": "bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832",
"email": "jane_doe@example.com",
"phone": "+12015550123",
"usernames": ["jdoe", "janed"],
"homePage": "http://www.example.com/jdoe/",
"joined": @2016-01-23,
"balance": $0.90,
"charge": $0.30
}
Native language support: Java (BigDecimal
), C#, Python, Ruby, …
SURF Has Lots of Types
- Regular Expression:
/a?b+c*/
- IRI/URI/URL/URN:
<http://example.com/>
- UUID:
&5623962b-22b1-4680-ae1c-7174a46144fc
- Email Address:
^jdoe@example.com
- Telephone Number:
+12015550123
- Binary:
%Zm9vYmFy
Rich Types with SURF
{
"name": "Jane Doe",
"id": &bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832,
"email": ^jane_doe@example.com,
"phone": +12015550123,
"usernames": ["jdoe", "janed"],
"homePage": <http://www.example.com/jdoe/>,
"joined": @2016-01-23,
"balance": $0.90,
"charge": $0.30
}
Look Mom, no commas!
{
"name": "Jane Doe"
"id": &bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832
"email": ^jane_doe@example.com
"phone": +12015550123
"usernames": [
"jdoe"
"janed"
]
"homePage": <http://www.example.com/jdoe/>
"joined": @2016-01-23
}
Compact with Commas
{"name":"Jane Doe","id":&bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832,
"email":^jane_doe@example.com,"phone":+12015550123,"usernames":
["jdoe","janed"],"homePage":<http://www.example.com/jdoe/>,
"joined":@2016-01-23,"balance":$0.90,"charge": $0.30}
Clearer with !
Comments
{
"name": "Jane Doe"
"id": &bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832
"email": ^jane_doe@example.com
"phone": +12015550123
"usernames": ["jdoe", "janed"]
"homePage": <http://www.example.com/jdoe/>
"joined": @2016-01-23
"balance": $0.90 !current account balance
"charge": $0.30 !monthly charge
}
JSON objects
aren't really.
JSON objects
are maps.
{
"name": "Jane Doe",
"id": "bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832",
"email": "jane_doe@example.com",
"phone": "+12015550123",
"usernames": ["jdoe", "janed"],
"homePage": "http://www.example.com/jdoe/",
"joined": "2016-01-23",
"balance": 0.90,
"charge": 0.30
}
JSON only allows string for map keys.
SURF Has Real Objects
A SURF object looks like this:
*
Think: An object instance.
A SURF object can have real property assignments.
*:
name = "Jane Doe"
;
A SURF object can have a type.
*User:
name = "Jane Doe"
;
Think: An instance of
User
described as follows: …
SURF requires *
for every object instance.
The separate TURF format allows object types themselves to be described, e.g. User:…;
.
A Real User
Object in SURF
*User:
name = "Jane Doe"
id = &bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832
email = ^jane_doe@example.com
phone = +12015550123
usernames = ["jdoe", "janed"]
homePage = <http://www.example.com/jdoe/>
joined = @2016-01-23
balance = $0.90
charge = $0.30
;
SURF Has Collections
- list
- set
- map
SURF parsers map collections to native programming language implementations.
Perfect fit for Java collection types!
Even JavaScript has real map and set types nowadays.
Lists Like You Like
*Rainbow:
colors = ["red", "orange", "yellow", "green",
"blue", "indigo", "violet"]
;
Just like JSON.
Maps Make a Return
*Person:
favoriteThings = {
5: "This person's favorite number."
"aliquot": "This person's favorite word."
}
;
Just like JSON… but without key type restrictions.
SURF Sets Make Sense
*User:
name = "Jane Doe"
id = &bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832
email = ^jane_doe@example.com
phone = +12015550123
usernames = ("jdoe", "janed")
homePage = <http://www.example.com/jdoe/>
joined = @2016-01-23
balance = $0.90
charge = $0.30
;
Here a set makes more semantic sense for the usernames
because it is unique and unordered.
Reference Resources with SURF Labels: Aliases and Tags
SURF Alias |foo|
for Internal Referencing
*Game:
players = [
|doe|*User:
name = "Jane Doe"
;
|smith|*User:
name = "John Smith"
;
]
winner = |doe| !reference an existing instance
;
Tag |<IRI>|
for External Referencing
|<urn:uuid:bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832>|*User:
name = "Jane Doe"
id = &bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832
email = ^jane_doe@example.com
phone = +12015550123
usernames = ("jdoe", "janed")
homePage = <http://www.example.com/jdoe/>
joined = @2016-01-23
balance = $0.90
charge = $0.30
;
Short form: |<&bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832>|
Reference Tags, Too
*Game:
players = [
|<&bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832>|*User:
name = "Jane Doe"
;
|<&d5207ff8-f64e-46b9-8fd9-d84ce8e0ff29>|*User:
name = "John Doe"
;
]
winner = |<&bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832>|
;
Tags are accessible even outside of SURF documents.
SURF Names
User
homePage
SURF Namespaces (Optional)
Prevent name clashes by with namespace prefixes.
chem-salt
Salt (chemistry)*:chem-salt="monosodium glutamate";
crypto-salt
Salt (cryptography)*:crypto-salt=%Zm9vYmFy;
A SURF name with namespace prefix is a handle.
No namespace declarations in SURF.
SURF parsers need no knowledge of namespaces.
Mix Vocabularies Using Namespaces
|<&bb8e7dbe-f0b4-4d94-a1cf-46ed0e920832>|*example-User:
name = "Jane Doe"
email = ^jane_doe@example.com
crypto-salt=%Zm9vYmFy
;
When creating a namespace, try to use an identifier from a domain or second-level domain you control.
The original version of this presentation was entitled From JSON to SURF in Java and was first shown at SeaJUG on 2017-05-16.
Thanks to Nimret Sandhu and all the members of SeaJUG for the opportunity and hospitality.