Closed IAmRasputin closed 2 years ago
(defvar *json* (parse "{\"1\": \"one\", \"2\": \"two\"}"))
(stringify *json* :pretty t :stream *standard-output* :replacer (lambda (k v)
(if (equal key "2")
(string-upcase v)
v)))
Should print:
{
"1": "one",
"2": "TWO"
}
With the most recent commit, the replacer function now returns:
T if the KV pair is to be included NIL if the KV pair is to be excluded completely (values t \<new value>) to change the value to stringify. For example:
(defvar *test-json* "{\"one\":[1,2,3],\"two\":{\"three\":\"four\"}}")
(defvar *parsed-json* (parse *test-json*))
(stringify *parsed-json*
:stream *standard-output*
:pretty t
:replacer (lambda (k v)
(cond
((equal k "three") nil)
((arrayp v) (values t (subseq v 0 2)))
(t t)))
=>
{
"one": [
1,
2,
],
"two": {}
}
@IAmRasputin FYI I wrote up a handful of tests for this new replacer. Could you add these to jzon-tests.lisp ?
(test stringify-replacer-keeps-keys-on-t
(is (string= "{\"x\":0}"
(stringify (ph :x 0)
:replacer (lambda (k v)
(declare (ignore k v))
t)))))
(test stringify-replacer-filters-keys-on-nil
(is (string= "{}"
(stringify (ph :x 0)
:replacer (lambda (k v)
(declare (ignore k v))
nil)))))
(test stringify-replacer-filters-some-keys-on-nil
(is (string= "{\"y\":0}"
(stringify (ph :x 0 :y 0)
:replacer (lambda (k v)
(declare (ignore v))
(eq k :y))))))
(test stringify-replacer-replaces-values-using-multiple-values
(is (string= "{\"x\":42}"
(stringify (ph :x 0)
:replacer (lambda (k v)
(declare (ignore k v))
(values t 42))))))
(test stringify-replacer-ignores-second-value-on-nil
(is (string= "{}"
(stringify (ph :x 0)
:replacer (lambda (k v)
(declare (ignore k v))
(values nil 42))))))
(test stringify-replacer-is-called-on-sub-objects
(is (string= "{\"x\":{\"a\":42}}"
(stringify (ph :x (ph :a 0))
:replacer (lambda (k v)
(declare (ignore v))
(if (eq k :a)
(values t 42)
t))))))
(test stringify-replacer-is-called-recursively
(is (string= "{\"x\":{\"y\":{\"z\":0}}}"
(stringify (ph :x 0)
:replacer (lambda (k v)
(declare (ignore v))
(case k
(:x (values t (ph :y 0)))
(:y (values t(ph :z 0)))
(t t)))))))
Added! And they pass on my machine, at least.
Yeah I have the auto-running on CI. So the last 2 things I'm seeing missing here is
replacer
should be called (with key
being nil
) on the top level valuereplacer
should be called on array entries, using the index of each entry as the key
value.
Here's two unit tests for those, but I also needed to update the existing tests.(test stringify-replacer-keeps-keys-on-t
(is (string= "{\"x\":0}"
(stringify (ph :x 0)
:replacer (lambda (k v)
(declare (ignore k v))
t)))))
(test stringify-replacer-filters-keys-on-nil
(is (string= "{}"
(stringify (ph :x 0)
:replacer (lambda (k v)
(declare (ignore v))
(case k
((nil) t)
(t nil)))))))
(test stringify-replacer-filters-some-keys-on-nil
(is (string= "{\"y\":0}"
(stringify (ph :x 0 :y 0)
:replacer (lambda (k v)
(declare (ignore v))
(case k
((nil) t)
(t (eq k :y))))))))
(test stringify-replacer-replaces-values-using-multiple-values
(is (string= "{\"x\":42}"
(stringify (ph :x 0)
:replacer (lambda (k v)
(declare (ignore v))
(case k
((nil) t)
(t (values t 42))))))))
(test stringify-replacer-ignores-second-value-on-nil
(is (string= "{}"
(stringify (ph :x 0)
:replacer (lambda (k v)
(declare (ignore v))
(case k
((nil) t)
(t (values nil 42))))))))
(test stringify-replacer-is-called-on-sub-objects
(is (string= "{\"x\":{\"a\":42}}"
(stringify (ph :x (ph :a 0))
:replacer (lambda (k v)
(declare (ignore v))
(if (eq k :a)
(values t 42)
t))))))
(test stringify-replacer-is-called-recursively
(is (string= "{\"x\":{\"y\":{\"z\":0}}}"
(stringify (ph :x 0)
:replacer (lambda (k v)
(declare (ignore v))
(case k
(:x (values t (ph :y 0)))
(:y (values t(ph :z 0)))
(t t)))))))
(test stringify-replacer-is-called-on-toplevel-value-with-nil-key
(5am:is-true
(let ((key-is-nil nil))
(stringify 0 :replacer (lambda (k v)
(declare (ignore v))
(setf key-is-nil (null k))))
key-is-nil))
(5am:is-true
(let ((value 0)
(value-is-same nil))
(stringify value :replacer (lambda (k v)
(declare (ignore k))
(setf value-is-same (eq value v) )))
value-is-same)))
(test stringify-replacer-can-replace-toplevel-value
(is (string= "42" (stringify 0 :replacer (lambda (k v)
(declare (ignore k v))
(values t 42))))))
(test stringify-replacer-is-called-on-array-elements-with-element-indexes
(is (equal #(0 1 2)
(let ((keys (list)))
(stringify #(t t t) :replacer (lambda (k v)
(declare (ignore v))
(when k
(push k keys))))
(nreverse keys)))))
By the way thank you so much for this!
This latest commit at least passes the tests. I also added test.sh
for non-windows convenience.
Nice, one last thing @IAmRasputin I think the code as written will end up calling replacer on every value recursively.
I think you can fix it by doing (and (null context) %replacer)
Inside of the write-value
method
Like this?
Yes, that does it I think!
here's a test for that case:
(test stringify-replacer-is-only-called-with-nil-on-toplevel-value
(is (equalp '(#(1 2 3))
(let ((called-on (list)))
(stringify '#(1 2 3) :replacer (lambda (k v)
(when (null k)
(push v called-on))
t))
(nreverse called-on)))))
Adds a slot to the
json-writer
class specifying a function to apply to key-value pairs during stringification.