-
Notifications
You must be signed in to change notification settings - Fork 29
/
doc.xml
774 lines (697 loc) · 31.8 KB
/
doc.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="clixdoc.xsl"?>
<clix:documentation xmlns:clix="http://bknr.net/clixdoc"
xmlns="http://www.w3.org/1999/xhtml">
<clix:title>YASON - A JSON encoder/decoder for Common Lisp</clix:title>
<clix:short-description>
YASON is a JSON encoding and decoding library for Common Lisp. It
provides for functions to read JSON strings into Lisp data
structures and for serializing Lisp data structures as JSON
strings.
</clix:short-description>
<clix:abstract>
YASON is a Common Lisp library for encoding and decoding data in
the <a href="http://json.org/">JSON</a> interchange format. JSON
is used as a lightweight alternative to XML. YASON has the sole
purpose of encoding and decoding data and does not impose any
object model on the Common Lisp application that uses it.
</clix:abstract>
<clix:contents/>
<clix:chapter name="intro" title="Introduction">
<p>
<a href="http://json.org/">JSON</a> is an established
alternative to XML as a data interchange format for web
applications. YASON implements reading and writing of JSON
formatted data in Common Lisp. It does not attempt to provide a
mapping between CLOS objects and YASON, but can be used to
implement such mappings.
</p>
<p>
<a href="http://common-lisp.net/project/cl-json/">CL-JSON</a> is
another Common Lisp package that can be used to work with JSON
encoded data. It takes a more integrated approach, providing
for library internal mappings between JSON objects and CLOS
objects. YASON was created as a lightweight, documented
alternative with a minimalistic approach and extensibilty.
</p>
</clix:chapter>
<clix:chapter name="install" title="Download and Installation">
<p>
YASON has its permanent home at <a
href="https://github.com/hanshuebner/yason/">GitHub</a>.
It can be obtained by downloading the <a
href="https://github.com/downloads/hanshuebner/yason/yason.tar.gz">release
tarball</a>. The current release is <clix:current-release/>.
</p>
<p>
You may also check out the current development version from its
<a href="http://github.com/hanshuebner/yason/">git
repository</a>. If you have suggestions regarding YASON, please
email me at <i>[email protected]</i>.
</p>
<p>
YASON is written in ANSI Common Lisp. It depends on UNIT-TEST,
TRIVIAL-GRAY-STREAMS and ALEXANDRIA open source libraries. The
recommended way to install YASON and its dependencies is through
the excellent <a href="http://www.quicklisp.org/">Quicklisp</a>
library management system.
</p>
<p>
YASON lives in the <b>:yason</b> package and creates a package
nickname <b>:json</b>. Applications will not normally
<b>:use</b> this package, but rather use qualified names to
access YASON's symbols. For that reason, YASON's symbols do not
contain the string "JSON" themselves. See below for usage
samples.
</p>
</clix:chapter>
<clix:chapter name="test" title="Running Tests">
<p>
To run tests, load the system and run this in the REPL:
<pre>CL-USER> (asdf:test-system :yason)</pre>
</p>
</clix:chapter>
<clix:chapter name="json-package" title="Using JSON as package name">
Versions of YASON preceding the v0.6.0 release provided a package
nickname "JSON" for the "YASON" package. This made it impossible
to load both YASON and CL-JSON into the same image, because
CL-JSON uses the "JSON" package name as well.
<p>
As CL-JSON's use of "JSON" as package name has a much longer
history and loading of both CL-JSON and YASON into the same
image has become more common, the "JSON" nickname was removed
from the YASON package with the v0.6.0 release. Users will need
to change their applications so that the "JSON" nickname is no
longer used to refer to the "YASON" package. It is understood
that this is a disruptive change, but as there is no
all-encompassing workaround, this step was felt to be the right
one to make
</p>
</clix:chapter>
<clix:chapter name="mapping" title="Mapping between JSON and CL datatypes">
By default, YASON performs the following mappings between JSON and
CL datatypes:
<table border="1">
<thead>
<tr>
<th>JSON<br/>datatype</th>
<th>CL<br/>datatype</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>object</td>
<td>hash-table<br/>:test #'equal</td>
<td>
Keys are strings by default (see
<clix:ref>*parse-object-key-fn*</clix:ref>). Set
<clix:ref>*parse-object-as*</clix:ref> to <b>:alist</b> in
order to have YASON parse objects as alists or to
<b>:plist</b> to parse them as plists. When using plists,
you probably want to also set
<clix:ref>*parse-object-key-fn*</clix:ref> to a function
that interns the object's keys to symbols.
</td>
</tr>
<tr>
<td>array</td>
<td>list</td>
<td>
Can be changed to read to vectors (see
<clix:ref>*parse-json-arrays-as-vectors*</clix:ref>).
</td>
</tr>
<tr>
<td>string</td>
<td>string</td>
<td>
JSON escape characters are recognized upon reading. Upon
writing, known escape characters are used, but non-ASCII
Unicode characters are written as is.
</td>
</tr>
<tr>
<td>number</td>
<td>number</td>
<td>
Parsed with READ, printed with PRINC. This is not a
faithful implementation of the specification.
</td>
</tr>
<tr>
<td>true</td>
<td>t</td>
<td>
Can be changed to read as TRUE (see
<clix:ref>*parse-json-booleans-as-symbols*</clix:ref>).
</td>
</tr>
<tr>
<td>false</td>
<td>nil</td>
<td>
Can be changed to read as FALSE (see
<clix:ref>*parse-json-booleans-as-symbols*</clix:ref>).
</td>
</tr>
<tr>
<td>null</td>
<td>nil</td>
<td></td>
</tr>
</tbody>
</table>
</clix:chapter>
<clix:chapter name="parsing" title="Parsing JSON data">
<p>
JSON data is always completely parsed into an equivalent
in-memory representation. Upon reading, some translations are
performed by default to make it easier for the Common Lisp
program to work with the data; see <clix:ref>mapping</clix:ref>
for details. If desired, the parser can be configured to
preserve the full semantics of the JSON data read.
</p>
For example
<pre>CL-USER> (defvar *json-string* "[{\"foo\":1,\"bar\":[7,8,9]},2,3,4,[5,6,7],true,null]")
*JSON-STRING*
CL-USER> (let* ((result (yason:parse *json-string*)))
(print result)
(alexandria:hash-table-plist (first result)))
(#<HASH-TABLE :TEST EQUAL :COUNT 2 {5A4420F1}> 2 3 4 (5 6 7) T NIL)
("bar" (7 8 9) "foo" 1)
CL-USER> (defun maybe-convert-to-keyword (js-name)
(or (find-symbol (string-upcase js-name) :keyword)
js-name))
MAYBE-CONVERT-TO-KEYWORD
CL-USER> :FOO ; intern the :FOO keyword
:FOO
CL-USER> (let* ((yason:*parse-json-arrays-as-vectors* t)
(yason:*parse-json-booleans-as-symbols* t)
(yason:*parse-object-key-fn* #'maybe-convert-to-keyword)
(result (yason:parse *json-string*)))
(print result)
(alexandria:hash-table-plist (aref result 0)))
#(#<HASH-TABLE :TEST EQUAL :COUNT 2 {59B4EAD1}> 2 3 4 #(5 6 7) YASON:TRUE NIL)
("bar" #(7 8 9) :FOO 1)</pre>
<p>
The second example modifies the parser's behaviour so that JSON
arrays are read as CL vectors, JSON booleans will be read as the
symbols TRUE and FALSE and JSON object keys will be looked up in
the <b>:keyword</b> package. Interning strings coming from an
external source is not recommended practice.
</p>
<clix:subchapter name="parser-dict" title="Parser dictionary">
<clix:function name="parse">
<clix:lambda-list>input &key (object-key-fn
*parse-object-as-key-fn*) (object-as *parse-object-as*)
(json-arrays-as-vectors *parse-json-arrays-as-vectors*)
(json-booleans-as-symbols *parse-json-booleans-as-symbols*)
(json-nulls-as-keyword *parse-json-null-as-keyword*)</clix:lambda-list>
<clix:returns>object</clix:returns>
<clix:description>
Parse <clix:arg>input</clix:arg>, which must be a string or
a stream, as JSON. Returns the Lisp representation of the
JSON structure parsed.
<p>
The keyword arguments <clix:arg>object-key-fn</clix:arg>,
<clix:arg>object-as</clix:arg>,
<clix:arg>json-arrays-as-vectors</clix:arg>,
<clix:arg>json-booleans-as-symbols</clix:arg>, and
<clix:arg>json-null-as-keyword</clix:arg> may be used
to specify different values for the parsing parameters
from the current bindings of the respective special
variables.
</p>
</clix:description>
</clix:function>
<clix:special-variable name="*parse-json-arrays-as-vectors*">
<clix:description>
If set to a true value, JSON arrays will be parsed as
vectors, not as lists. NIL is the default.
</clix:description>
</clix:special-variable>
<clix:special-variable name="*parse-object-as*">
<clix:description>
Can be set to <b>:hash-table</b> to parse objects as hash
tables, <b>:alist</b> to parse them as alists or
<b>:plist</b> to parse them as plists. <b>:hash-table</b>
is the default.
</clix:description>
</clix:special-variable>
<clix:special-variable name="*parse-json-booleans-as-symbols*">
<clix:description>
If set to a true value, JSON booleans will be read as the
symbols TRUE and FALSE instead of T and NIL, respectively.
NIL is the default.
</clix:description>
</clix:special-variable>
<clix:special-variable name="*parse-json-null-as-keyword*">
<clix:description>
If set to a true value, JSON null will be read as the
keyword :NULL, instead of NIL.
NIL is the default.
</clix:description>
</clix:special-variable>
<clix:special-variable name="*parse-object-key-fn*">
<clix:description>
Function to call to convert a key string in a JSON object to
a key in the CL hash produced. IDENTITY is the default.
</clix:description>
</clix:special-variable>
</clix:subchapter>
<p>
To avoid <a href="https://bishopfox.com/blog/json-interoperability-vulnerabilities">security issues</a>
in Common Lisp code and/or downstream, on parsing JSON duplicate keys
are rejected. If allowing that is a real use case, please send a patch
providing a restart.
</p>
</clix:chapter>
<clix:chapter name="encoding" title="Encoding JSON data">
YASON provides two distinct modes to encode JSON data:
applications can either create an in-memory representation of the
data to be serialized, then have YASON convert it to JSON in one
go, or they can use a set of macros to serialize the JSON data
element-by-element, allowing fine-grained control over the layout
of the generated data.
<p>
Optionally, the JSON that is produced can be indented.
Indentation requires the use of a
<clix:ref>JSON-OUTPUT-STREAM</clix:ref> as serialization target.
With the stream serializer, such a stream is automatically used.
If indentation is desired with the DOM serializer, such a stream
can be obtained by calling the
<clix:ref>MAKE-JSON-OUTPUT-STREAM</clix:ref> function with the
target output string as argument. Please be aware that indented
output not requires more space, but is also slower and should
not be enabled in performance critical applications.
</p>
<clix:subchapter name="dom-encoder" title="Encoding a JSON DOM">
<p>
In this mode, an in-memory structure is encoded in JSON
format. The structure must consist of objects that are
serializable using the <clix:ref>ENCODE</clix:ref> function.
YASON defines a number of encoders for standard data types
(see <clix:ref>MAPPING</clix:ref>), but the application can
define additional methods (e.g. for encoding CLOS objects).
</p>
For example:
<pre>CL-USER> (yason:encode
(list (alexandria:plist-hash-table
'("foo" 1 "bar" (7 8 9))
:test #'equal)
2 3 4
'(5 6 7)
t nil)
*standard-output*)
[{"foo":1,"bar":[7,8,9]},2,3,4,[5,6,7],true,null]
(#<HASH-TABLE :TEST EQUAL :COUNT 2 {59942D21}> 2 3 4 (5 6 7) T NIL)</pre>
<clix:subchapter name="dom-encoder-dict" title="DOM encoder dictionary">
<clix:function name="encode" generic="true">
<clix:lambda-list>object &optional
stream</clix:lambda-list>
<clix:returns>object</clix:returns>
<clix:description>
Encode <clix:arg>object</clix:arg> in JSON format and
write to <clix:arg>stream</clix:arg>. May be specialized
by applications to perform specific rendering. Stream
defaults to *STANDARD-OUTPUT*.
</clix:description>
</clix:function>
<clix:function name="encode-alist">
<clix:lambda-list>object &optional (stream
*standard-output*)</clix:lambda-list>
<clix:returns>object</clix:returns>
<clix:description>
Encodes <clix:arg>object</clix:arg>, an alist, in JSON
format and write to <clix:arg>stream</clix:arg>.
</clix:description>
</clix:function>
<clix:function name="encode-plist">
<clix:lambda-list>object &optional (stream
*standard-output*)</clix:lambda-list>
<clix:returns>object</clix:returns>
<clix:description>
Encodes <clix:arg>object</clix:arg>, a plist, in JSON
format and write to <clix:arg>stream</clix:arg>.
</clix:description>
</clix:function>
<clix:function name="make-json-output-stream">
<clix:lambda-list>stream &key (indent t)</clix:lambda-list>
<clix:returns>stream</clix:returns>
<clix:description>
Creates a <clix:ref>json-output-stream</clix:ref> instance
that wraps the supplied <clix:arg>stream</clix:arg> and
optionally performs indentation of the generated JSON
data. The <clix:arg>indent</clix:arg> argument is
described in <clix:ref>WITH-OUTPUT</clix:ref>. Note that
if the <clix:arg>indent</clix:arg> argument is NIL, the
original stream is returned in order to avoid the
performance penalty of the indentation algorithm.
</clix:description>
</clix:function>
<clix:special-variable name="*list-encoder*">
<clix:description>
Function to call to translate a CL list into JSON data.
<clix:arg>'YASON:ENCODE-PLAIN-LIST-TO-ARRAY</clix:arg> is the default;
<clix:arg>'YASON:ENCODE-PLIST</clix:arg> and
<clix:arg>'YASON:ENCODE-ALIST</clix:arg> are available to produce
JSON objects.
<p>
This is useful to translate a deeply recursive structure in a single
<clix:arg>YASON:ENCODE</clix:arg> call.
</p>
</clix:description>
</clix:special-variable>
<clix:special-variable name="*symbol-encoder*">
<clix:description>
Function to call to translate a CL symbol into a JSON string.
The default is to error out, to provide backwards-compatible behaviour.
<p>
A useful function that can be bound to this variable is
<clix:ref>YASON:ENCODE-SYMBOL-AS-LOWERCASE.</clix:ref>
</p>
</clix:description>
</clix:special-variable>
<clix:special-variable name="*symbol-key-encoder*">
<clix:description>
Defines the policy to encode symbols as keys (eg. in hash tables).
The default is to error out, to provide backwards-compatible behaviour.
<p>
A useful function that can be bound to this variable is
<clix:ref>YASON:ENCODE-SYMBOL-AS-LOWERCASE.</clix:ref>
</p>
</clix:description>
</clix:special-variable>
</clix:subchapter>
</clix:subchapter>
<clix:subchapter name="stream-encoder" title="Encoding JSON in streaming mode">
<p>
In this mode, the JSON structure is generated in a stream.
The application makes explicit calls to the encoding library
in order to generate the JSON structure. It provides for more
control over the generated output, and can be used to generate
arbitary JSON without requiring that there exists a directly
matching Lisp data structure. The streaming API uses the
<clix:ref>encode</clix:ref> function, so it is possible to
intermix the two (see <clix:ref>app-encoders</clix:ref> for an
example).
</p>
For example:
<pre>CL-USER> (yason:with-output (*standard-output*)
(yason:with-array ()
(dotimes (i 3)
(yason:encode-array-element i))))
[0,1,2]
NIL
CL-USER> (yason:with-output (*standard-output*)
(yason:with-object ()
(yason:encode-object-element "hello" "hu hu")
(yason:with-object-element ("harr")
(yason:with-array ()
(dotimes (i 3)
(yason:encode-array-element i))))))
{"hello":"hu hu","harr":[0,1,2]}
NIL</pre>
<clix:subchapter name="stream-encoder-dict" title="Streaming encoder dictionary">
<clix:function name="with-output" macro="true">
<clix:lambda-list>(stream &key indent) &body body</clix:lambda-list>
<clix:returns>result*</clix:returns>
<clix:description>
Set up a JSON streaming encoder context on
<clix:arg>stream</clix:arg>, then evaluate
<clix:arg>body</clix:arg>. <clix:arg>indent</clix:arg>
can be set to T to enable indentation with a default
indentation width or to an integer specifying the desired
indentation width. By default, indentation is switched
off.
</clix:description>
</clix:function>
<clix:function name="with-output-to-string*" macro="true">
<clix:lambda-list>(&key indent stream-symbol) &body body</clix:lambda-list>
<clix:returns>result*</clix:returns>
<clix:description>
Set up a JSON streaming encoder context on
<clix:arg>stream-symbol</clix:arg> (by default
a gensym), then evaluate
<clix:arg>body</clix:arg>. Return a string with the
generated JSON output. See
<clix:ref>WITH-OUTPUT</clix:ref> for the description of
the <clix:arg>indent</clix:arg> keyword argument.
</clix:description>
</clix:function>
<clix:condition name="no-json-output-context">
<clix:description>
This condition is signalled when one of the stream
encoding functions is used outside the dynamic context of
a <clix:ref>WITH-OUTPUT</clix:ref> or
<clix:ref>WITH-OUTPUT-TO-STRING*</clix:ref> body.
</clix:description>
</clix:condition>
<clix:function name="with-array" macro="true">
<clix:lambda-list>() &body body</clix:lambda-list>
<clix:returns>result*</clix:returns>
<clix:description>
Open a JSON array, then run <clix:arg>body</clix:arg>.
Inside the body, <clix:ref>ENCODE-ARRAY-ELEMENT</clix:ref>
must be called to encode elements to the opened array.
Must be called within an existing JSON encoder context
(see <clix:ref>WITH-OUTPUT</clix:ref> and
<clix:ref>WITH-OUTPUT-TO-STRING*</clix:ref>).
</clix:description>
</clix:function>
<clix:function name="encode-array-element">
<clix:lambda-list>object</clix:lambda-list>
<clix:returns>object</clix:returns>
<clix:description>
Encode <clix:arg>object</clix:arg> as next array element to
the last JSON array opened
with <clix:ref>WITH-ARRAY</clix:ref> in the dynamic
context. <clix:arg>object</clix:arg> is encoded using the
<clix:ref>ENCODE</clix:ref> generic function, so it must be of
a type for which an <clix:ref>ENCODE</clix:ref> method is
defined.
</clix:description>
</clix:function>
<clix:function name="encode-array-elements">
<clix:lambda-list>&rest objects</clix:lambda-list>
<clix:returns>result*</clix:returns>
<clix:description>
Encode <clix:arg>objects</clix:arg>, a series of JSON
encodable objects, as the next array elements in a JSON
array opened with
<clix:ref>WITH-ARRAY</clix:ref>. ENCODE-ARRAY-ELEMENTS
uses <clix:ref>ENCODE-ARRAY-ELEMENT</clix:ref>, which must
be applicable to each object in the list
(i.e. <clix:ref>ENCODE</clix:ref> must be defined for each
object type). Additionally, this must be called within a
valid stream context.
</clix:description>
</clix:function>
<clix:function name="with-object" macro="true">
<clix:lambda-list>() &body body</clix:lambda-list>
<clix:returns>result*</clix:returns>
<clix:description>
Open a JSON object, then run <clix:arg>body</clix:arg>.
Inside the body,
<clix:ref>ENCODE-OBJECT-ELEMENT</clix:ref> or
<clix:ref>WITH-OBJECT-ELEMENT</clix:ref> must be called to
encode elements to the object. Must be called within an
existing JSON encoder
<clix:ref>WITH-OUTPUT</clix:ref> and
<clix:ref>WITH-OUTPUT-TO-STRING*</clix:ref>.
</clix:description>
</clix:function>
<clix:function name="with-object-element" macro="true">
<clix:lambda-list>(key) &body body</clix:lambda-list>
<clix:returns>result*</clix:returns>
<clix:description>
Open a new encoding context to encode a JSON object
element. <clix:arg>key</clix:arg> is the key of the
element. The value will be whatever
<clix:arg>body</clix:arg> serializes to the current JSON
output context using one of the stream encoding functions.
This can be used to stream out nested object structures.
</clix:description>
</clix:function>
<clix:function name="encode-object-element">
<clix:lambda-list>key value</clix:lambda-list>
<clix:returns>value</clix:returns>
<clix:description>
Encode <clix:arg>key</clix:arg> and
<clix:arg>value</clix:arg> as object element to the last
JSON object opened with <clix:ref>WITH-OBJECT</clix:ref>
in the dynamic context. <clix:arg>key</clix:arg> and
<clix:arg>value</clix:arg> are encoded using the
<clix:ref>ENCODE</clix:ref> generic function, so they both
must be of a type for which an <clix:ref>ENCODE</clix:ref>
method is defined.
</clix:description>
</clix:function>
<clix:function name="encode-object-elements">
<clix:lambda-list>&rest elements</clix:lambda-list>
<clix:returns>result*</clix:returns>
<clix:description>
Encodes the parameters into JSON in the last object opened
with <clix:ref>WITH-OBJECT</clix:ref> using
<clix:ref>ENCODE-OBJECT-ELEMENT</clix:ref>. The parameters
should consist of alternating key/value pairs, and this
must be called within a valid stream context.
</clix:description>
</clix:function>
<clix:function name="encode-object-slots">
<clix:lambda-list>object slots</clix:lambda-list>
<clix:returns>result*</clix:returns>
<clix:description>
Encodes each slot in SLOTS for OBJECT in the last object
opened with <clix:ref>WITH-OBJECT</clix:ref> using
<clix:ref>ENCODE-OBJECT-ELEMENT</clix:ref>. The key is the
slot name, and the value is the slot value for the slot on
OBJECT. It is equivalent to
<pre>(loop for slot in slots
do (encode-object-element (string slot)
(slot-value object slot)))
</pre>
</clix:description>
</clix:function>
<clix:function name="encode-slots">
<clix:lambda-list>object</clix:lambda-list>
<clix:returns>result*</clix:returns>
<clix:description>
Generic function to encode object slots. There is no default
implementation.
It should be called in an object encoding context. It uses
PROGN combinatation with MOST-SPECIFIC-LAST order, so that
base class slots are encoded before derived class slots.
</clix:description>
</clix:function>
<clix:function name="encode-object">
<clix:lambda-list>object</clix:lambda-list>
<clix:returns>result*</clix:returns>
<clix:description>
Generic function to encode an object. The default implementation
opens a new object encoding context and calls
<clix:ref>ENCODE-SLOTS</clix:ref> on the argument.
</clix:description>
</clix:function>
<clix:class name="json-output-stream">
<clix:description>
Instances of this class are used to wrap an output stream
that is used as a serialization target in the stream
encoder and optionally in the DOM encoder if indentation
is desired. The class name is not exported, use
<clix:ref>make-json-output-stream</clix:ref> to create a
wrapper stream if required.
</clix:description>
</clix:class>
</clix:subchapter>
</clix:subchapter>
<clix:subchapter name="app-encoders" title="Application specific encoders">
Suppose your application uses structs to represent its data and
you want to encode these structs using JSON in order to send
them to a client application. Suppose further that your structs
also include internal information that you do not want to send.
Here is some code that illustrates how one could implement a
serialization function:
<pre>CL-USER> (defstruct user name age password)
USER
CL-USER> (defmethod yason:encode ((user user) &optional (stream *standard-output*))
(yason:with-output (stream)
(yason:with-object ()
(yason:encode-object-element "name" (user-name user))
(yason:encode-object-element "age" (user-age user)))))
#<STANDARD-METHOD YASON:ENCODE (USER) {5B40A591}>
CL-USER> (yason:encode (list (make-user :name "horst" :age 27 :password "puppy")
(make-user :name "uschi" :age 28 :password "kitten")))
[{"name":"horst","age":27},{"name":"uschi","age":28}]
(#S(USER :NAME "horst" :AGE 27 :PASSWORD "puppy")
#S(USER :NAME "uschi" :AGE 28 :PASSWORD "kitten"))</pre>
As you can see, the streaming API and the DOM encoder can be
used together. <clix:ref>ENCODE</clix:ref> invokes itself
recursively, so any application defined method will be called
while encoding in-memory objects as appropriate.
<p>For an example of the interplay between
<clix:ref>ENCODE-OBJECT</clix:ref> and
<clix:ref>ENCODE-SLOTS</clix:ref>, suppose you have the following
CLOS class heirarchy:
<pre>(defclass shape ()
((color :reader color)))
(defclass square (shape)
((side-length :reader side-length)))
(defclass circle (shape)
((radius :reader radius)))</pre>
In order to implement encoding of circles and squares without
duplicating code you can specialize
<clix:ref>ENCODE-SLOTS</clix:ref> for all three classes
<pre>(defmethod yason:encode-slots progn ((shape shape))
(yason:encode-object-element "color" (color shape)))
(defmethod yason:encode-slots progn ((square square))
(yason:encode-object-element "side-length" (side-length square)))
(defmethod yason:encode-slots progn ((circle circle))
(yason:encode-object-element "radius" (radius circle)))</pre>
and then use <clix:ref>ENCODE-OBJECT</clix:ref>:
<pre>CL-USER> (yason:with-output-to-string* ()
(yason:encode-object (make-instance 'square :color "red" :side-length 3)))
"{\"color\":\"red\",\"side-length\":3}"
CL-USER> (yason:with-output-to-string* ()
(yason:encode-object (make-instance 'circle :color "blue" :side-length 5)))
"{\"color\":\"blue\",\"radius\":5}"</pre>
</p>
<p>Alternatively, you can use the shortcut <clix:ref>ENCODE-OBJECT-SLOTS</clix:ref>
if you want the keys to be the slot names. For example:
<pre>(defclass person ()
((name :reader name :initarg :name)
(address :reader address :initarg :address)
(phone-number :reader phone-number :initarg :phone)
(favorite-color :reader favorite-color :initarg :color)))
(defmethod yason:encode-slots progn ((person person))
(yason:encode-object-slots person '(name address phone-number favorite-color)))</pre>
and then:
<pre>CL-USER> (yason:with-output-to-string* ()
(yason:encode-object (make-instance 'person :name "John Doe"
:address "123 Main St."
:phone "(123)-456-7890"
:color "blue")))
"{\"NAME\":\"John Doe\",\"ADDRESS\":\"123 Main St.\",\"PHONE-NUMBER\":\"(123)-456-7890\",
\"FAVORITE-COLOR\":\"blue\"}"</pre>
</p>
</clix:subchapter>
</clix:chapter>
<clix:chapter name="index" title="Symbol index">
<clix:index/>
</clix:chapter>
<clix:chapter name="license" title="License">
<pre class="none">Copyright (c) 2008-2014 Hans Hübner and contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
- Neither the name BKNR nor the names of its contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</pre>
</clix:chapter>
<clix:chapter name="ack" title="Acknowledgements">
Thanks go to Edi Weitz for being a great inspiration. This
documentation as been generated with a hacked-up version of his <a
href="http://weitz.de/documentation-template/">DOCUMENTATION-TEMPLATE</a>
software. Thanks to David Lichteblau for coining YASON's name.
</clix:chapter>
</clix:documentation>