7Data sent between our agent and our front-end will all be encoded as JSON. In
8this section, we'll briefly look at how JSON works in Urbit, and write a library
9to convert our agent's structures to and from JSON for our front-end.
11JSON data comes into Eyre as a string, and Eyre parses it with the
12[`++de-json:html`](/docs/hoon/reference/zuse/2e_2-3#de-jsonhtml) function in
13[`zuse.hoon`](/docs/hoon/reference/zuse/table-of-contents). The
14hoon type it's parsed to is `$json`, which is defined as:
17+$ json :: normal json value
19 $% [%a p=(list json)] :: array
21 [%o p=(map @t json)] :: object
27Once Eyre has converted the raw JSON string to a `$json` structure, it will be
28converted to the mark the web client specified and then delivered to the target
29agent (unless the mark specified is already `%json`, in which case it will be
30delivered directly). Outbound facts will go through the same process in
31reverse - converted from the agent's native mark to `$json`, then encoded in a
33[`++en-json:html`](<(/docs/hoon/reference/zuse/2e_2-3#en-jsonhtml)>) and delivered
34to the web client. The basic flow for both inbound messages (pokes) and outbound
35messages (facts and scry results) looks like this:
37
39The mark conversion will be done by the corresponding mark file in `/mar` on the
40agent's desk. In our case it would be `/mar/journal/action.hoon` and
41`/mar/journal/update.hoon` in the `%journal` desk for our `%journal-action` and
42`%journal-update` marks, which are for the `$action` and `$update` structures we
45Mark conversion functions can be included directly in the mark file, or they can
46be written in a separate library, then imported and called by the mark file. We
47will do the latter in this case, so before we create the mark files themselves,
48we'll write a library called `/lib/journal.hoon` with the conversion functions.
52[`zuse.hoon`](/docs/hoon/reference/zuse/table-of-contents) contains three main
53cores for converting to and from `$json`:
55- [`++enjs:format`](/docs/hoon/reference/zuse/2d_1-5#enjsformat) - Functions to
56 help encode data structures as `$json`.
57- [`++dejs:format`](/docs/hoon/reference/zuse/2d_6#dejsformat) - Functions to
58 decode `$json` to other data structures.
59- [`++dejs-soft:format`](/docs/hoon/reference/zuse/2d_7#dejs-softformat) -
60 Mostly the same as `++dejs:format` except the functions produce units which
61 are null if decoding fails, rather than just crashing.
65This contains ten functions for encoding `$json`. Most of them are for specific
66hoon data types, such as `++tape:enjs:format`, `++ship:enjs:format`,
67`++path:enjs:format`, etc. We'll just have a look at the two most general and
68useful ones: `++frond:enjs:format` and `++pairs:enjs:format`.
72This function is for forming a JSON object from a single key-value pair. For
76> (frond:enjs:format 'foo' s+'bar')
77[%o p={[p='foo' q=[%s p='bar']]}]
80When stringified by Eyre, this will look like:
88This is similar to `++frond` and also forms a JSON object, but it takes multiple
89key-value pairs rather than just one:
92> (pairs:enjs:format ~[['foo' n+~.123] ['bar' s+'abc'] ['baz' b+&]])
93[%o p={[p='bar' q=[%s p='abc']] [p='baz' q=[%b p=%.y]] [p='foo' q=[%n p=~.123]]}]
96When stringified by Eyre, this will look like:
106Notice that we used a knot for the value of `foo` (`n+~.123`). Numbers in JSON
107can be signed or unsigned and integers or floating point values. The `$json`
108structure uses a knot so that you can decide whether a particular number should
109be treated as `@ud`, `@sd`, `@rs`, etc.
113This core contains many functions for decoding `$json`. We'll touch on some
114useful families of `++dejs` functions in brief, but because there's so many, in
115practice you'll need to look through the [`++dejs`
116reference](/docs/hoon/reference/zuse/2d_6) to find the correct functions for
121- `++ne` - decode a number to a `@rd`.
122- `++ni` - decode a number to a `@ud`.
123- `++no` - decode a number to a `@ta`.
124- `++nu` - decode a hexadecimal string to a `@ux`.
129> (ni:dejs:format n+'123')
135- `++sa` - decode a string to a `tape`.
136- `++sd` - decode a string containing a `@da` aura date value to a `@da`.
137- `++se` - decode a string containing the specified aura to that aura.
138- `++so` - decode a string to a `@t`.
139- `++su` - decode a string by parsing it with the given [parsing
140 rule](/docs/hoon/reference/stdlib/4f).
144`++ar`, `++as`, and `++at` decode a `$json` array to a `list`, `set`, and
145n-tuple respectively. These gates take other `++dejs` functions as an argument,
146producing a new gate that will then take the `$json` array. For example:
149> ((ar so):dejs:format a+[s+'foo' s+'bar' s+'baz' ~])
153Notice that `++so` is given as the argument to `++ar`. `++so` is a `++dejs`
154function that decodes a `$json` string to a `cord`. The gate resulting from `(ar so)` is then called with a `$json` array as its argument, and its product is a
155`(list @t)` of the elements of the array.
157Many `++dejs` functions take other `++dejs` functions as their arguments. A
158complex nested `$json` decoding function can be built up in this manner.
162- `++of` - decode an object containing a single key-value pair to a head-tagged
164- `++ot` - decode an object to a n-tuple.
165- `++ou` - decode an object to an n-tuple, replacing optional missing values
167- `++oj` - decode an object of arrays to a `jug`.
168- `++om` - decode an object to a `map`.
169- `++op` - decode an object to a `map`, and also parse the object keys with a
170 [parsing rule](/docs/hoon/reference/stdlib/4f).
175> =js %- need %- de-json:html
184> %- (ot ~[foo+so bar+ni]):dejs:format js
190We need to decide how our `$action` and `$update` types will be represented as
191JSON in order to write our conversion functions. There are many ways to do this,
192but in this case we'll do it as follows:
197| ------------------------------------------------- | ---------------------------------------------- |
198| `{"add":{"id":1648366311070,"txt":"some text"}}` | `[%add id=1.648.366.034.844 txt='some text']` |
199| `{"edit":{"id":1648366311070,"txt":"some text"}}` | `[%edit id=1.648.366.034.844 txt='some text']` |
200| `{"del":{"id":1648366311070}}` | `[%del id=1.648.366.034.844]` |
205| ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
206| `[1.648.366.492.459 %add id=1.648.366.034.844 txt='some text']` | `{time:1648366481425,"add":{"id":1648366311070,"txt":"some text"}}` |
207| `[1.648.366.492.459 %edit id=1.648.366.034.844 txt='some text']` | `{time:1648366481425,"edit":{"id":1648366311070,"txt":"some text"}}` |
208| `[1.648.366.492.459 %del id=1.648.366.034.844]` | `{time:1648366481425,"del":{"id":1648366311070}}` |
209| `[1.648.366.492.459 %jrnl ~[[id=1.648.366.034.844 txt='some text'] ...]` | `{time:1648366481425,"entries":[{"id":1648366311070,"txt":"some text"},...]}` |
210| `[1.648.366.492.459 %logs ~[[1.648.366.492.459 %add id=1.648.366.034.844 txt='some text'] ...]` | `{time:1648366481425,"logs":[{time:1648366481425,"add":{id":1648366311070,"txt":"some text"}},...]}` |
212Now let's write our library of encoding/decoding functions.
214## `/lib/journal.hoon`
221First, we'll import the `/sur/journal.hoon` structures we previously created.
222Next, we'll create two arms in our core, `++dejs-action` and `++enjs-update`, to
223handle incoming poke `$action`s and outgoing facts or scry result `$update`s.
225### `$json` to `$action`
234 :~ [%add (ot ~[id+ni txt+so])]
235 [%edit (ot ~[id+ni txt+so])]
240The first thing we do is use the [`=,`
241rune](/docs/hoon/reference/rune/tis#-tiscom) to expose the `++dejs:format`
242namespace. This allows us to reference `ot`, `ni`, etc rather than having to
243write `ot:dejs:format` every time. Note that you should be careful using `=,`
244generally as the exposed wings can shadow previous wings if they have the same
247We then create a gate that takes `$json` and returns a `$action` structure.
248Since we'll only take one action at a time, we can use the `++of` function,
249which takes a single key-value pair. `++of` takes a list of all possible `$json`
250objects it will receive, tagged by key.
252For each key, we specify a function to handle its value. Ours will be objects,
253so we use `++ot` and specify the pairs of the key and `+dejs` function to decode
254it. We then cast the output to our `$action` structure.
256You'll notice the nesting of these `++dejs` functions approximately reflects the
257nested structure of the `$json` it's decoding.
259### `$update` to `$json`
267 ?+ -.q.upd (logged upd)
270 :~ ['time' (numb p.upd)]
271 ['entries' a+(turn list.q.upd entry)]
276 :~ ['time' (numb p.upd)]
277 ['logs' a+(turn list.q.upd logged)]
284 :~ ['id' (numb id.ent)]
293 :~ ['time' (numb p.lgd)]
296 :~ ['id' (numb id.q.lgd)]
301 :~ ['time' (numb p.lgd)]
304 :~ ['id' (numb id.q.lgd)]
309 :~ ['time' (numb p.lgd)]
311 (frond 'id' (numb id.q.lgd))
318Our `$update` encoding function's a little more complex than our `$action`
319decoding function, since our `$update` structure is more complex.
321Like the previous one, we use `=,` to expose the namespace of `++enjs:format`.
323Our gate takes an `$update` and returns a `$json` structure. We use `|^` so we
324can separate out the encoding functions for individual entries (`++entry`) and
325individual logged actions (`++logged`).
327We first test the head of the `$update`, and if it's `%jrnl` (a list of
328entries), we `turn` over the entries and call `++entry` to encode each one. If
329it's `%logs`, we do the same, but call `++logged` for each item in the list.
330Otherwise, if it's just a single update, we encode it with `++logged`.
332We primarily use `++pairs` to form the object, though sometimes `++frond` if it
333only contains a single key-value pair. We also use `++numb` to encode numerical
336You'll notice more of our encoding function is done manually than our previous
337decoding function. For example, we form arrays by tagging an ordinary `list`
338with `%a`, and strings by tagging an ordinary `cord` with `%s`. This is typical
339when you write `$json` encoding functions, and is the reason there are far fewer
340`+enjs` functions than `+dejs` functions.
344- [The JSON Guide](/docs/hoon/guides/json-guide) - The stand-alone JSON guide
345 covers JSON encoding/decoding in great detail.
346- [The Zuse reference](/docs/hoon/reference/zuse/table-of-contents) - The
347 `zuse.hoon` reference documents all JSON-related functions in detail.
349- [`++enjs:format` reference](/docs/hoon/reference/zuse/2d_1-5#enjsformat) -
350 This section of the `zuse.hoon` documentation covers all JSON encoding
353- [`++dejs:format` reference](/docs/hoon/reference/zuse/2d_6) - This section of
354 the `zuse.hoon` documentation covers all JSON _decoding_ functions.
356- [Eyre overview](/docs/arvo/eyre/eyre) - This section of the Eyre vane
357 documentation goes over the basic features of the Eyre vane.
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list