Revision: 4050 Author: hans URL: http://bknr.net/trac/changeset/4050
Add HTML rendered version of the documentation.
A trunk/libraries/yason/index.html
Added: trunk/libraries/yason/index.html =================================================================== --- trunk/libraries/yason/index.html (rev 0) +++ trunk/libraries/yason/index.html 2008-11-15 10:12:08 UTC (rev 4050) @@ -0,0 +1,477 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Strict//EN"> +<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> +<title>YASON - A JSON encoder/decoder for Common Lisp</title><meta name="description" content=" + 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. + "></meta><style type="text/css"> + body { background-color: #ffffff; max-width: 50em; margin-left: 2em; font-family: Georgia, 'Times New Roman', serif; } + pre { padding:5px; background-color:#e0e0e0; margin: 1em 2em 1em 2em; } + pre.none { padding:5px; background-color:#ffffff; } + h3, h4, h5 { text-decoration: underline; } + a { text-decoration: none; padding: 1px 2px 1px 2px; } + a:visited { text-decoration: none; padding: 1px 2px 1px 2px; } + a:hover { text-decoration: none; padding: 1px 1px 1px 1px; border: 1px solid #000000; } + a:focus { text-decoration: none; padding: 1px 2px 1px 2px; border: none; } + a.none { text-decoration: none; padding: 0; } + a.none:visited { text-decoration: none; padding: 0; } + a.none:hover { text-decoration: none; border: none; padding: 0; } + a.none:focus { text-decoration: none; border: none; padding: 0; } + a.noborder { text-decoration: none; padding: 0; } + a.noborder:visited { text-decoration: none; padding: 0; } + a.noborder:hover { text-decoration: none; border: none; padding: 0; } + a.noborder:focus { text-decoration: none; border: none; padding: 0; } + table { margin: 1em; } + </style></head><body> + + <h2 xmlns="">YASON - A JSON encoder/decoder for Common Lisp</h2> + + + + <h3 xmlns="">Abstract</h3> +<blockquote xmlns=""> + YASON is a Common Lisp library for encoding and decoding data in + the <a xmlns="http://www.w3.org/1999/xhtml" href="http://json.org/">JSON</a> interchange format. + JSON is used in AJAX applications 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. + </blockquote> + + <h3 xmlns="">Contents</h3> +<ol xmlns=""> +<li><a href="#intro">Introduction</a></li> +<li><a href="#install">Installation</a></li> +<li><a href="#mapping">Mapping between JSON and CL datatypes</a></li> +<li> +<a href="#parsing">Parsing JSON data</a><ol><li><a href="#parser-dict">Parser dictionary</a></li></ol> +</li> +<li> +<a href="#encoding">Encoding JSON data</a><ol> +<li><a href="#dom-encoder">Encoding a JSON DOM</a></li> +<li><a href="#stream-encoder">Encoding JSON in streaming mode</a></li> +<li><a href="#app-encoders">Application specific encoders</a></li> +</ol> +</li> +<li><a href="#index">Symbol index</a></li> +<li><a href="#license">License</a></li> +<li><a href="#ack">Acknowledgements</a></li> +</ol> + + <h3 xmlns=""><a class="none" name="intro">Introduction</a></h3> + <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. + + + <h3 xmlns=""><a class="none" name="install">Installation</a></h3> + <p> + YASON has its permanent home + at <a href="http://common-lisp.net/project/yason/">common-lisp.net</a>. + It can be obtained either by downloading + the <a href="http://common-lisp.net/project/yason/files/yason.tar.gz">release + tarball</a> or by checking out the current development version + from its subversion repository: + <pre>svn co svn://bknr.net/svn/trunk/libraries/yason/</pre> + </p> + <p> + YASON is written in ANSI Common Lisp and does not depend on + other libraries. + </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> + + + <h3 xmlns=""><a class="none" name="mapping">Mapping between JSON and CL datatypes</a></h3> + By default, YASON performs the following mappings between JSON and + CL datatypes: + <table border="1"> + <thead> + <tr> + <th>JSON<br></br>datatype</th> + <th>CL<br></br>datatype</th> + <th>Notes</th> + </tr> + </thead> + <tbody> + <tr> + <td>object</td> + <td>hash-table<br></br>:test #'equal</td> + <td> + Keys are strings by default, + see <code xmlns=""><a href="#*parse-object-key-fn*">*parse-object-key-fn*</a></code> + </td> + </tr> + <tr> + <td>array</td> + <td>list</td> + <td> + Can be changed to read to vectors, + see <code xmlns=""><a href="#*parse-json-arrays-as-vectors*">*parse-json-arrays-as-vectors*</a></code> + </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 <code xmlns=""><a href="#*parse-json-booleans-as-symbols*">*parse-json-booleans-as-symbols*</a></code></td> + </tr> + <tr> + <td>false</td> + <td>nil</td> + <td>Can be changed to read as FALSE, see <code xmlns=""><a href="#*parse-json-booleans-as-symbols*">*parse-json-booleans-as-symbols*</a></code></td> + </tr> + <tr> + <td>null</td> + <td>nil</td> + <td></td> + </tr> + </tbody> + </table> + + + <h3 xmlns=""><a class="none" name="parsing">Parsing JSON data</a></h3> + <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 <code xmlns=""><a href="#mapping">mapping</a></code> + 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 (json: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* ((json:*parse-json-arrays-as-vectors* t) + (json:*parse-json-booleans-as-symbols* t) + (json:*parse-object-key-fn* #'maybe-convert-to-string) + (result (json: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> + + <h4 xmlns=""><a name="parser-dict">Parser dictionary</a></h4> + <p xmlns="">[Generic function]<br><a class="none" name="parse"><b>parse</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">input</clix:lambda-list></i> + => + <i>object</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc%22%3E + Parse <code><i>input</i></code>, which needs to be a string + or a stream, as JSON. Returns the lisp representation of the + JSON structure parsed. + </clix:description></blockquote></p> + + <p xmlns=""> + [Special variable]<br><a class="none" name="*parse-json-arrays-as-vectors*"><b>*parse-json-arrays-as-vectors*</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc%22%3E + If set to a true value, JSON arrays will be parsed as vectors, + not as lists. + </clix:description></blockquote></p> + + <p xmlns=""> + [Special variable]<br><a class="none" name="*parse-json-booleans-as-symbols*"><b>*parse-json-booleans-as-symbols*</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc%22%3E + If set to a true value, JSON booleans will be read as the + symbols TRUE and FALSE, not as T and NIL, respectively. + </clix:description></blockquote></p> + + <p xmlns=""> + [Special variable]<br><a class="none" name="*parse-object-key-fn*"><b>*parse-object-key-fn*</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc%22%3E + Function to call to convert a key string in a JSON array to a + key in the CL hash produced. + </clix:description></blockquote></p> + + + + <h3 xmlns=""><a class="none" name="encoding">Encoding JSON data</a></h3> + YASON provides for 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 serialze the JSON data + element-by-element, thereby having fine grained control over the + layout of the generated data. + + <h4 xmlns=""><a name="dom-encoder">Encoding a JSON DOM</a></h4> + <p> + In this mode, an in-memory structure is encoded in JSON format. + The structure must consist of objects that are serializable + using the <code xmlns=""><a href="#encode">ENCODE</a></code> function. YASON defines a + number of encoders for standard data types + (see <code xmlns=""><a href="#mapping">MAPPING</a></code>), but the application can + define additional methods, e.g. for encoding CLOS objects. + </p> + For example: + <pre>CL-USER> (json: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> + + <h4 xmlns=""><a name="dom-encoder-dict">DOM encoder dictionary</a></h4> + <p xmlns="">[Generic function]<br><a class="none" name="encode"><b>encode</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">object &optional stream</clix:lambda-list></i> + => + <i>object</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc%22%3E + Encode <code><i>object</i></code> + to <code><i>stream</i></code> in JSON format. May be + specialized by applications to perform specific + rendering. <code><i>stream</i></code> defaults to + *STANDARD-OUTPUT*. + </clix:description></blockquote></p> + + + + <h4 xmlns=""><a name="stream-encoder">Encoding JSON in streaming mode</a></h4> + <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 datastructure. The streaming API uses + the <code xmlns=""><a href="#encode">encode</a></code> function, so it is possible to + intermix the two. See <code xmlns=""><a href="#app-encoders">app-encoders</a></code> for + an example. + </p> + For example: + <pre>CL-USER> (json:with-output (*standard-output*) + (json:with-array () + (dotimes (i 3) + (json:encode-array-element i)))) +[0,1,2] +NIL +CL-USER> (json:with-output (*standard-output*) + (json:with-object () + (json:encode-object-element "hello" "hu hu") + (json:with-object-element ("harr") + (json:with-array () + (dotimes (i 3) + (json:encode-array-element i)))))) +{"hello":"hu hu","harr":[0,1,2]} +NIL</pre> + + <h4 xmlns=""><a name="stream-encoder-dict">Streaming encoder dictionary</a></h4> + <p xmlns="">[Macro]<br><a class="none" name="with-output"><b>with-output</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">(stream) &body body</clix:lambda-list></i> + => + <i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc%22%3E + Set up a JSON streaming encoder context + on <code><i>stream</i></code>, then + evaluate <code><i>body</i></code>. + </clix:description></blockquote></p> + + <p xmlns="">[Macro]<br><a class="none" name="with-output-to-string*"><b>with-output-to-string*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">() &body body</clix:lambda-list></i> + => + <i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc%22%3E + Set up a JSON streaming encoder context, then + evaluate <code><i>body</i></code>. Return a string with the + generated JSON output. + </clix:description></blockquote></p> + + <p xmlns=""> + [Condition type]<br><a class="none" name="no-json-output-context"><b>no-json-output-context</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc%22%3E + This condition is signalled when one of the stream encoding + function is used outside the dynamic context of a + <code><a href="#with-output">WITH-OUTPUT</a></code> or + <code><a href="#with-output-to-string*">WITH-OUTPUT-TO-STRING*</a></code> body. + </clix:description></blockquote></p> + + <p xmlns="">[Macro]<br><a class="none" name="with-array"><b>with-array</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">() &body body</clix:lambda-list></i> + => + <i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc%22%3E + Open a JSON array, then run <code><i>body</i></code>. Inside + the body, <code><a href="#encode-array-element">ENCODE-ARRAY-ELEMENT</a></code> must be + called to encode elements to the opened array. Must be called + within an existing JSON encoder context, see + <code><a href="#with-output">WITH-OUTPUT</a></code> and + <code><a href="#with-output-to-string*">WITH-OUTPUT-TO-STRING*</a></code>. + </clix:description></blockquote></p> + + <p xmlns="">[Function]<br><a class="none" name="encode-array-element"><b>encode-array-element</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">object</clix:lambda-list></i> + => + <i>object</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc%22%3E + Encode <code><i>object</i></code> as next array element to + the last JSON array opened + with <code><a href="#with-array">WITH-ARRAY</a></code> in the dynamic + context. <code><i>object</i></code> is encoded using the + <code><a href="#encode">ENCODE</a></code> generic function, so it must be of + a type for which an <code><a href="#encode">ENCODE</a></code> method is + defined. + </clix:description></blockquote></p> + + <p xmlns="">[Macro]<br><a class="none" name="with-object"><b>with-object</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">() &body body</clix:lambda-list></i> + => + <i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc%22%3E + Open a JSON object, then run <code><i>body</i></code>. Inside the body, + <code><a href="#encode-object-element">ENCODE-OBJECT-ELEMENT</a></code> + or <code><a href="#with-object-element">WITH-OBJECT-ELEMENT</a></code> must be called to + encode elements to the object. Must be called within an + existing JSON encoder context, + see <code><a href="#with-output">WITH-OUTPUT</a></code> + and <code><a href="#with-output-to-string*">WITH-OUTPUT-TO-STRING*</a></code>. + </clix:description></blockquote></p> + + <p xmlns="">[Macro]<br><a class="none" name="with-object-element"><b>with-object-element</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">(key) &body body</clix:lambda-list></i> + => + <i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc%22%3E + Open a new encoding context to encode a JSON object + element. <code><i>key</i></code> is the key of the element. + The value will be whatever <code><i>body</i></code> + 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></blockquote></p> + + <p xmlns="">[Function]<br><a class="none" name="encode-object-element"><b>encode-object-element</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">key value</clix:lambda-list></i> + => + <i>value</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc%22%3E + Encode <code><i>key</i></code> and <code><i>value</i></code> + as object element to the last JSON object opened + with <code><a href="#with-object">WITH-OBJECT</a></code> in the dynamic + context. <code><i>key</i></code> + and <code><i>value</i></code> are encoded using + the <code><a href="#encode">ENCODE</a></code> generic function, so they both + must be of a type for which an <code><a href="#encode">ENCODE</a></code> + method is defined. + </clix:description></blockquote></p> + + + + <h4 xmlns=""><a name="app-encoders">Application specific encoders</a></h4> + + Suppose your application uses structs to represent its data, and + you want to encode such structs using JSON in order to send it + 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 json:encode ((user user) &optional (stream *standard-output*)) + (json:with-output (stream) + (json:with-object () + (json:encode-object-element "name" (user-name user)) + (json:encode-object-element "age" (user-age user))))) +#<STANDARD-METHOD YASON:ENCODE (USER) {5B40A591}> +CL-USER> (json: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. <code xmlns=""><a href="#encode">ENCODE</a></code> invokes itself + recursively, so any application defined method will be called + while encoding in-memory objects as appropriate. + + + + + <h3 xmlns=""><a class="none" name="index">Symbol index</a></h3> + <ul xmlns=""> +<li><code><a href="#*parse-json-arrays-as-vectors*">*parse-json-arrays-as-vectors*</a></code></li> +<li><code><a href="#*parse-json-booleans-as-symbols*">*parse-json-booleans-as-symbols*</a></code></li> +<li><code><a href="#*parse-object-key-fn*">*parse-object-key-fn*</a></code></li> +<li><code><a href="#encode">encode</a></code></li> +<li><code><a href="#encode-array-element">encode-array-element</a></code></li> +<li><code><a href="#encode-object-element">encode-object-element</a></code></li> +<li><code><a href="#no-json-output-context">no-json-output-context</a></code></li> +<li><code><a href="#parse">parse</a></code></li> +<li><code><a href="#with-array">with-array</a></code></li> +<li><code><a href="#with-object">with-object</a></code></li> +<li><code><a href="#with-object-element">with-object-element</a></code></li> +<li><code><a href="#with-output">with-output</a></code></li> +<li><code><a href="#with-output-to-string*">with-output-to-string*</a></code></li> +</ul> + + + <h3 xmlns=""><a class="none" name="license">License</a></h3> + <pre class="none">Copyright (c) 2008 Hans Hübner +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> + + + <h3 xmlns=""><a class="none" name="ack">Acknowledgements</a></h3> + 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. + + +</body></html>