janet-lang / spork

Various Janet utility modules - the official "Contrib" library.
MIT License
117 stars 35 forks source link

`json/encode` and representing objects with `null` values #159

Closed sogaiu closed 10 months ago

sogaiu commented 10 months ago

It appears that json/encode can produce buffers that have null in them in some situations:

(json/encode nil)
# =>
@"null"
(json/encode [1 2 nil 9])
# =>
@"[1,2,null,9]"

However, it's not clear to me whether one can produce something that's supposed to represent an object (note that the examples above involve a string and an array) with a null value in it. That is, something like:

@`{"result":null}`

and not:

@`{"result":"null"}`

At some point, CFiggers' janet-lsp fork had a work-around for this situation:

(defn success-response [id result]
  (if (nil? result)
    (string "{\"id\":" id ",\"result\":null,\"jsonrpc\":\"2.0\"}")
    (json/encode {:jsonrpc "2.0"
                  :id id
                  :result result})))

For reference, here is a link to the beginning of a related discussion on Zulip.

bakpakin commented 10 months ago

The function json/decode has an optional parameter nils which lets you change the behavior with how to encode null. The default is :null, which will not cause a loss of information, but you can pass in a truthy value to get a normal nil.

From (doc json/decode):

cfunction

    (json/decode json-source &opt keywords nils)

    Returns a janet object after parsing JSON. If keywords is truthy, string keys will be
    converted to keywords. If nils is truthy, null will become nil instead of the keyword :null.
sogaiu commented 10 months ago

I'm confused.

How does the capability of json/decode help with respect to json/encode?


IIUC, json/encode is able to produce buffers representing some types of JSON values that have null in them, but it's not clear to me how to do this for JSON objects.

For JSON null and JSON arrays it seems good -- but is it possible to do this for JSON objects?

These work fine:

(json/encode nil)
# =>
@"null"
(json/encode [1 2 nil 9])
# =>
@"[1,2,null,9]"

But how can I end up with:

@`{"result":null}`

and not:

@`{"result":"null"}`

For some background, IIUC it's the LSP specification which mandates use of such values:

The result property of the ResponseMessage should be set to null in this case to signal a successful request.

This came up in the context of janet-lsp.

bakpakin commented 10 months ago

Should be fixed in d644da0fd05612a2d5a3c97277bf7b9bb96dcf6b - now passing :null to json/encode will result in a null in the JSON.

sogaiu commented 10 months ago

Seems to work:

(json/encode {:result :null})
# =>
@"{\"result\":null}"

Thank you!