sbcl stuff
This commit is contained in:
parent
1d1dbc34df
commit
5d91dbb667
335 changed files with 119806 additions and 1 deletions
1
sbcl/.quicklisp/dists/quicklisp/software/yason-v0.7.8/.gitignore
vendored
Normal file
1
sbcl/.quicklisp/dists/quicklisp/software/yason-v0.7.8/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
*.*f*sl
|
||||
|
|
@ -0,0 +1 @@
|
|||
sh render-doc.sh
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
Version 0.7.6
|
||||
2016-01-26
|
||||
Revert Rick's changes for alist/plist behavior (Hans Huebner)
|
||||
|
||||
Version 0.7.5
|
||||
2015-06-14
|
||||
Fixed release script (Hans Huebner)
|
||||
|
||||
Version 0.7.4
|
||||
2015-06-14
|
||||
Remove post-release.sh that updated c-l.net (Hans Huebner)
|
||||
|
||||
Version 0.7.3
|
||||
2015-06-14
|
||||
Fix #28 Add ENCODE-OBJECT-SLOTS (Thayne McCombs)
|
||||
Update html documentation (Hans Huebner)
|
||||
update documentation link (Hans Huebner)
|
||||
Documentation work (Hans Huebner)
|
||||
|
||||
Version 0.7.2
|
||||
2014-12-05
|
||||
Avoid using e notation because Lisp, JS and JSON numeral syntaxes are incompatible. (Grim Schjetne)
|
||||
readd encode-object/encode-slots API with proper documentation (Philipp Matthias Schaefer)
|
||||
|
||||
Version 0.7.1
|
||||
2014-11-04
|
||||
Remove unused ENCODE-SLOTS and ENCODE-OBJECT stubs
|
||||
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
Copyright (c) 2008-2019 Hans Huebner 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.
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
YASON
|
||||
=====
|
||||
|
||||
> YASON is a Common Lisp library for encoding and decoding data in the
|
||||
> [JSON](https://raw.github.com/phmarek/clixdoc/master/clixdoc.xsl)
|
||||
> 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.
|
||||
|
||||
Please proceed to the [Documentation](http://phmarek.github.io/yason)
|
||||
|
||||
This project was maintained by https://github.com/hanshuebner/ until 2019.
|
||||
|
|
@ -0,0 +1,411 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1" ?>
|
||||
|
||||
<!--
|
||||
;;; 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.
|
||||
|
||||
;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
|
||||
;;; 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 AUTHOR 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.
|
||||
-->
|
||||
|
||||
<xsl:stylesheet
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
version="1.0"
|
||||
xmlns:clix="http://bknr.net/clixdoc"
|
||||
exclude-result-prefixes="clix">
|
||||
|
||||
<xsl:output method="html"
|
||||
indent="yes"
|
||||
omit-xml-declaration="yes"
|
||||
doctype-public="-//W3C//DTD HTML 4.0 Strict//EN" />
|
||||
|
||||
<xsl:template match="clix:current-release"><xsl:value-of select="$current-release"/></xsl:template>
|
||||
|
||||
<xsl:template match="/clix:documentation">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
<title><xsl:value-of select="clix:title"/></title>
|
||||
<meta name="description"><xsl:attribute name="content"><xsl:value-of select="clix:short-description"/></xsl:attribute></meta>
|
||||
<style type="text/css">
|
||||
body { background-color: #ffffff; max-width: 50em; margin-left: 2em; }
|
||||
blockquote { margin-left: 2em; margin-right: 2em; }
|
||||
pre { padding:5px; background-color:#e0e0e0 }
|
||||
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; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<xsl:apply-templates/>
|
||||
</body>
|
||||
</html>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:title">
|
||||
<h1><xsl:value-of select="."/></h1>
|
||||
</xsl:template>
|
||||
<xsl:template match="clix:short-description"/>
|
||||
|
||||
<xsl:template match="clix:function">
|
||||
<p>
|
||||
<xsl:choose>
|
||||
<xsl:when test="@generic = 'true'">[Generic function]</xsl:when>
|
||||
<xsl:when test="@specialized = 'true'">[Method]</xsl:when>
|
||||
<xsl:when test="@macro = 'true'">[Macro]</xsl:when>
|
||||
<xsl:otherwise>[Function]</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
<br/>
|
||||
<a class="none">
|
||||
<xsl:attribute name="name">
|
||||
<xsl:value-of select="translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
|
||||
</xsl:attribute>
|
||||
<b><xsl:value-of select="@name"/></b>
|
||||
<xsl:value-of select="' '"/>
|
||||
<i><xsl:apply-templates select="clix:lambda-list"/></i>
|
||||
<xsl:if test="clix:returns">
|
||||
=>
|
||||
<i><xsl:value-of select="clix:returns"/></i>
|
||||
</xsl:if>
|
||||
</a>
|
||||
<blockquote>
|
||||
<xsl:apply-templates select="clix:description"/>
|
||||
</blockquote>
|
||||
</p>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:reader">
|
||||
<p>
|
||||
<xsl:choose>
|
||||
<xsl:when test="@generic = 'true'">[Generic reader]</xsl:when>
|
||||
<xsl:when test="@specialized = 'true'">[Specialized reader]</xsl:when>
|
||||
<xsl:otherwise>[Reader]</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
<br/>
|
||||
<a class="none">
|
||||
<xsl:attribute name="name">
|
||||
<xsl:value-of select="translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
|
||||
</xsl:attribute>
|
||||
<b><xsl:value-of select="@name"/></b>
|
||||
<xsl:value-of select="' '"/>
|
||||
<i><xsl:apply-templates select="clix:lambda-list"/></i>
|
||||
<xsl:if test="clix:returns">
|
||||
=>
|
||||
<i><xsl:value-of select="clix:returns"/></i>
|
||||
</xsl:if>
|
||||
</a>
|
||||
<blockquote>
|
||||
<xsl:apply-templates select="clix:description"/>
|
||||
</blockquote>
|
||||
</p>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:writer">
|
||||
<p>
|
||||
<xsl:choose>
|
||||
<xsl:when test="@generic = 'true'">[Generic writer]</xsl:when>
|
||||
<xsl:when test="@specialized = 'true'">[Specialized writer]</xsl:when>
|
||||
<xsl:otherwise>[Writer]</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
<br/>
|
||||
<a class="none">
|
||||
<xsl:attribute name="name">
|
||||
<xsl:value-of select="translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
|
||||
</xsl:attribute>
|
||||
<tt>(setf (</tt><b><xsl:value-of select="@name"/></b>
|
||||
<xsl:value-of select="' '"/>
|
||||
<i><xsl:apply-templates select="clix:lambda-list"/></i><tt>) <i>new-value</i>)</tt>
|
||||
<xsl:if test="clix:returns">
|
||||
=>
|
||||
<i><xsl:value-of select="clix:returns"/></i>
|
||||
</xsl:if>
|
||||
</a>
|
||||
<blockquote>
|
||||
<xsl:apply-templates select="clix:description"/>
|
||||
</blockquote>
|
||||
</p>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:accessor">
|
||||
<p>
|
||||
<xsl:choose>
|
||||
<xsl:when test="@generic = 'true'">[Generic accessor]</xsl:when>
|
||||
<xsl:when test="@specialized = 'true'">[Specialized accessor]</xsl:when>
|
||||
<xsl:otherwise>[Accessor]</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
<br/>
|
||||
<a class="none">
|
||||
<xsl:attribute name="name">
|
||||
<xsl:value-of select="translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
|
||||
</xsl:attribute>
|
||||
<b><xsl:value-of select="@name"/></b>
|
||||
<xsl:value-of select="' '"/>
|
||||
<i><xsl:apply-templates select="clix:lambda-list"/></i>
|
||||
=>
|
||||
<i><xsl:value-of select="clix:returns"/></i>
|
||||
<br/>
|
||||
<tt>(setf (</tt><b><xsl:value-of select="@name"/></b>
|
||||
<xsl:value-of select="' '"/>
|
||||
<i><xsl:apply-templates select="clix:lambda-list"/></i><tt>) <i>new-value</i>)</tt>
|
||||
</a>
|
||||
<blockquote>
|
||||
<xsl:apply-templates select="clix:description"/>
|
||||
</blockquote>
|
||||
</p>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:special-variable">
|
||||
<p>
|
||||
[Special variable]<br/>
|
||||
<a class="none">
|
||||
<xsl:attribute name="name">
|
||||
<xsl:value-of select="translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
|
||||
</xsl:attribute>
|
||||
<b><xsl:value-of select="@name"/></b>
|
||||
</a>
|
||||
<blockquote>
|
||||
<xsl:apply-templates select="clix:description"/>
|
||||
</blockquote>
|
||||
</p>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:class">
|
||||
<p>
|
||||
[Standard class]<br/>
|
||||
<a class="none">
|
||||
<xsl:attribute name="name">
|
||||
<xsl:value-of select="translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
|
||||
</xsl:attribute>
|
||||
<b><xsl:value-of select="@name"/></b>
|
||||
</a>
|
||||
<blockquote>
|
||||
<xsl:apply-templates select="clix:description"/>
|
||||
</blockquote>
|
||||
</p>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:condition">
|
||||
<p>
|
||||
[Condition type]<br/>
|
||||
<a class="none">
|
||||
<xsl:attribute name="name">
|
||||
<xsl:value-of select="translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
|
||||
</xsl:attribute>
|
||||
<b><xsl:value-of select="@name"/></b>
|
||||
</a>
|
||||
<blockquote>
|
||||
<xsl:apply-templates select="clix:description"/>
|
||||
</blockquote>
|
||||
</p>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:symbol">
|
||||
<p>
|
||||
[Symbol]<br/>
|
||||
<a class="none">
|
||||
<xsl:attribute name="name">
|
||||
<xsl:value-of select="translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
|
||||
</xsl:attribute>
|
||||
<b><xsl:value-of select="@name"/></b>
|
||||
</a>
|
||||
<blockquote>
|
||||
<xsl:apply-templates select="clix:description"/>
|
||||
</blockquote>
|
||||
</p>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:constant">
|
||||
<p>
|
||||
[Constant]<br/>
|
||||
<a class="none">
|
||||
<xsl:attribute name="name">
|
||||
<xsl:value-of select="translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
|
||||
</xsl:attribute>
|
||||
<b><xsl:value-of select="@name"/></b>
|
||||
</a>
|
||||
<blockquote>
|
||||
<xsl:apply-templates select="clix:description"/>
|
||||
</blockquote>
|
||||
</p>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:listed-constant">
|
||||
<a class="none">
|
||||
<xsl:attribute name="name"><xsl:value-of select="@name"/></xsl:attribute>
|
||||
<b><xsl:value-of select="@name"/></b>
|
||||
</a>
|
||||
<br/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:constants">
|
||||
<!-- Display a list of constants with a common description -->
|
||||
<p>
|
||||
[Constants]<br/>
|
||||
<xsl:apply-templates select="clix:listed-constant"/>
|
||||
<blockquote>
|
||||
<xsl:apply-templates select="clix:description"/>
|
||||
</blockquote>
|
||||
</p>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:logical-pathname-host">
|
||||
<p>
|
||||
[Logical Pathname Host]<br/>
|
||||
<a class="none">
|
||||
<xsl:attribute name="name">
|
||||
<xsl:value-of select="translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
|
||||
</xsl:attribute>
|
||||
<b><xsl:value-of select="@name"/></b>
|
||||
</a>
|
||||
<blockquote>
|
||||
<xsl:apply-templates select="clix:description"/>
|
||||
</blockquote>
|
||||
</p>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:qualifier">
|
||||
<!-- method qualifier -->
|
||||
<tt><xsl:value-of select="text()"/></tt>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:lkw">
|
||||
<!-- lambda list keyword -->
|
||||
<tt>&<xsl:value-of select="text()"/></tt>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:arg">
|
||||
<!-- argument reference -->
|
||||
<code><i><xsl:value-of select="text()"/></i></code>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="internal-reference">
|
||||
<!-- internal reference -->
|
||||
<xsl:param name="name"/>
|
||||
<code>
|
||||
<a>
|
||||
<xsl:attribute name="href">
|
||||
#<xsl:value-of select="translate($name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
|
||||
</xsl:attribute>
|
||||
<xsl:value-of select="$name"/>
|
||||
</a>
|
||||
</code>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:ref">
|
||||
<xsl:call-template name="internal-reference">
|
||||
<xsl:with-param name="name"><xsl:value-of select="."/></xsl:with-param>
|
||||
</xsl:call-template>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:hyperspec">
|
||||
<a>
|
||||
<xsl:attribute name="href">http://www.lispworks.com/documentation/HyperSpec/Body/<xsl:value-of select="@link"/></xsl:attribute>
|
||||
<xsl:value-of select="."/>
|
||||
</a>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:chapter">
|
||||
<h3>
|
||||
<a class="none">
|
||||
<xsl:attribute name="name"><xsl:value-of select="@name"/></xsl:attribute>
|
||||
<xsl:value-of select="@title"/>
|
||||
</a>
|
||||
</h3>
|
||||
<xsl:apply-templates/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:subchapter">
|
||||
<h4>
|
||||
<a>
|
||||
<xsl:attribute name="name"><xsl:value-of select="@name"/></xsl:attribute>
|
||||
<xsl:value-of select="@title"/>
|
||||
</a>
|
||||
</h4>
|
||||
<xsl:apply-templates/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:abstract">
|
||||
<h3>Abstract</h3>
|
||||
<blockquote>
|
||||
<xsl:apply-templates/>
|
||||
</blockquote>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:contents">
|
||||
<h3>Contents</h3>
|
||||
<ol>
|
||||
<xsl:for-each select="//clix:chapter">
|
||||
<li>
|
||||
<a>
|
||||
<xsl:attribute name="href">#<xsl:value-of select="@name"/></xsl:attribute>
|
||||
<xsl:value-of select="@title"/>
|
||||
</a>
|
||||
<xsl:if test="clix:subchapter">
|
||||
<ol>
|
||||
<xsl:for-each select="clix:subchapter">
|
||||
<li>
|
||||
<a>
|
||||
<xsl:attribute name="href">#<xsl:value-of select="@name"/></xsl:attribute>
|
||||
<xsl:value-of select="@title"/>
|
||||
</a>
|
||||
</li>
|
||||
</xsl:for-each>
|
||||
</ol>
|
||||
</xsl:if>
|
||||
</li>
|
||||
</xsl:for-each>
|
||||
</ol>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="clix:index">
|
||||
<ul>
|
||||
<xsl:for-each select="(//clix:function | //clix:reader | //clix:writer | //clix:accessor | //clix:class | //clix:condition | //clix:constant | //clix:listed-constant | //clix:special-variable | //clix:symbol)">
|
||||
<xsl:sort select="@name"/>
|
||||
<li>
|
||||
<xsl:call-template name="internal-reference">
|
||||
<xsl:with-param name="name"><xsl:value-of select="@name"/></xsl:with-param>
|
||||
</xsl:call-template>
|
||||
</li>
|
||||
</xsl:for-each>
|
||||
</ul>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="*">
|
||||
<xsl:copy>
|
||||
<xsl:copy-of select="@*[not(namespace-uri())]"/>
|
||||
<xsl:apply-templates/>
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
||||
748
sbcl/.quicklisp/dists/quicklisp/software/yason-v0.7.8/doc.xml
Normal file
748
sbcl/.quicklisp/dists/quicklisp/software/yason-v0.7.8/doc.xml
Normal file
|
|
@ -0,0 +1,748 @@
|
|||
<?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>hans.huebner@gmail.com</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="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>
|
||||
</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 serialze 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-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>
|
||||
|
|
@ -0,0 +1,388 @@
|
|||
;; This file is part of yason, a Common Lisp JSON parser/encoder
|
||||
;;
|
||||
;; Copyright (c) 2008-2014 Hans Huebner and contributors
|
||||
;; All rights reserved.
|
||||
;;
|
||||
;; Please see the file LICENSE in the distribution.
|
||||
|
||||
(in-package :yason)
|
||||
|
||||
(defvar *json-output*)
|
||||
|
||||
(defparameter *default-indent* nil
|
||||
"Set to T or an numeric indentation width in order to have YASON
|
||||
indent its output by default.")
|
||||
|
||||
(defparameter *default-indent-width* 2
|
||||
"Default indentation width for output if indentation is selected
|
||||
with no indentation width specified.")
|
||||
|
||||
(defparameter *list-encoder* 'encode-plain-list-to-array
|
||||
"The actual function used to encode a LIST.
|
||||
Can be changed to encode ALISTs or PLISTs as dictionaries by
|
||||
setting it to ENCODE-ALIST or ENCODE-PLIST.")
|
||||
|
||||
(defparameter *symbol-key-encoder* 'encode-symbol-key-error
|
||||
"The actual function used to encode a SYMBOL when seen as a key.
|
||||
You might want ENCODE-SYMBOL-AS-LOWERCASE here.")
|
||||
|
||||
|
||||
(defgeneric encode (object &optional stream)
|
||||
|
||||
(:documentation "Encode OBJECT to STREAM in JSON format. May be
|
||||
specialized by applications to perform specific rendering. STREAM
|
||||
defaults to *STANDARD-OUTPUT*."))
|
||||
|
||||
(defparameter *char-replacements*
|
||||
(alexandria:plist-hash-table
|
||||
'(#\\ "\\\\"
|
||||
#\" "\\\""
|
||||
#\Backspace "\\b"
|
||||
#\Page "\\f"
|
||||
#\Newline "\\n"
|
||||
#\Return "\\r"
|
||||
#\Tab "\\t")))
|
||||
|
||||
(defun unicode-code (char)
|
||||
(char-code char))
|
||||
|
||||
(defun unicode-char (code)
|
||||
(code-char code))
|
||||
|
||||
(defun write-surrogate-pair-escape (code stream)
|
||||
(let ((upper (+ (ldb (byte 10 10) (- code #x10000))
|
||||
#xD800))
|
||||
(lower (+ (ldb (byte 10 0) (- code #x10000))
|
||||
#xDC00)))
|
||||
(format stream "\\u~4,'0X\\u~4,'0X" upper lower)))
|
||||
|
||||
(defmethod encode ((string string) &optional (stream *standard-output*))
|
||||
(write-char #\" stream)
|
||||
(dotimes (i (length string))
|
||||
(let* ((char (aref string i))
|
||||
(replacement (gethash char *char-replacements*)))
|
||||
(cond
|
||||
(replacement (write-string replacement stream))
|
||||
;; Control characters (U+0000 - U+001F) must be escaped.
|
||||
((<= #x0000 (unicode-code char) #x001F)
|
||||
(format stream "\\u~4,'0X" (unicode-code char)))
|
||||
;; Non-BMP characters must be escaped as a UTF-16 surrogate pair.
|
||||
((<= #x010000 (unicode-code char) #x10FFFF)
|
||||
(write-surrogate-pair-escape (unicode-code char) stream))
|
||||
(t (write-char char stream)))))
|
||||
(write-char #\" stream)
|
||||
string)
|
||||
|
||||
(defmethod encode ((object ratio) &optional (stream *standard-output*))
|
||||
(encode (coerce object 'double-float) stream)
|
||||
object)
|
||||
|
||||
(defmethod encode ((object float) &optional (stream *standard-output*))
|
||||
(let ((*read-default-float-format* 'double-float))
|
||||
(format stream "~F" (coerce object 'double-float)))
|
||||
object)
|
||||
|
||||
(defmethod encode ((object integer) &optional (stream *standard-output*))
|
||||
(princ object stream))
|
||||
|
||||
(defmacro with-aggregate/object ((stream opening-char closing-char) &body body)
|
||||
"Set up serialization context for aggregate serialization with the
|
||||
object encoder."
|
||||
(alexandria:with-gensyms (printed)
|
||||
`(progn
|
||||
(write-delimiter ,opening-char ,stream)
|
||||
(change-indentation ,stream #'+)
|
||||
(prog1
|
||||
(let (,printed)
|
||||
(macrolet ((with-element-output (() &body body)
|
||||
`(progn
|
||||
(cond
|
||||
(,',printed
|
||||
(write-delimiter #\, ,',stream))
|
||||
(t
|
||||
(setf ,',printed t)))
|
||||
(write-indentation ,',stream)
|
||||
,@body)))
|
||||
,@body))
|
||||
(change-indentation ,stream #'-)
|
||||
(write-indentation ,stream)
|
||||
(write-delimiter ,closing-char ,stream)))))
|
||||
|
||||
(defun encode-key/value (key value stream)
|
||||
(encode key stream)
|
||||
(write-char #\: stream)
|
||||
(encode value stream))
|
||||
|
||||
(defmethod encode ((object hash-table) &optional (stream *standard-output*))
|
||||
(with-aggregate/object (stream #\{ #\})
|
||||
(maphash (lambda (key value)
|
||||
(with-element-output ()
|
||||
(encode-key/value key value stream)))
|
||||
object)
|
||||
object))
|
||||
|
||||
(defmethod encode ((object vector) &optional (stream *standard-output*))
|
||||
(with-aggregate/object (stream #\[ #\])
|
||||
(loop for value across object
|
||||
do (with-element-output ()
|
||||
(encode value stream)))
|
||||
object))
|
||||
|
||||
(defun encode-plain-list-to-array (object stream)
|
||||
(with-aggregate/object (stream #\[ #\])
|
||||
(dolist (value object)
|
||||
(with-element-output ()
|
||||
(encode value stream)))
|
||||
object))
|
||||
|
||||
(defmethod encode ((object list) &optional (stream *standard-output*))
|
||||
(funcall *list-encoder* object stream))
|
||||
|
||||
|
||||
(defun encode-symbol-key-error (key)
|
||||
(error "No policy for symbols as keys defined. ~
|
||||
Please check YASON:*SYMBOL-KEY-ENCODER*."))
|
||||
|
||||
(defun encode-symbol-as-lowercase (key)
|
||||
"Encodes a symbol KEY as a lowercase string.
|
||||
Ensure that there's no intentional lower-case character lost."
|
||||
(let ((name (symbol-name key)))
|
||||
(assert (notany #'lower-case-p name))
|
||||
(string-downcase name)))
|
||||
|
||||
(defun encode-assoc-key/value (key value stream)
|
||||
;; Checking (EVERY #'UPPER-CASE-P name) breaks with non-alpha characters like #\-
|
||||
(let ((string (if (symbolp key)
|
||||
(funcall *symbol-key-encoder* key)
|
||||
(string key))))
|
||||
(encode-key/value string value stream)))
|
||||
|
||||
(defun encode-alist (object &optional (stream *standard-output*))
|
||||
;; Failsafe in case this here is not an ALIST but a normal list
|
||||
(if (consp (first object))
|
||||
(with-aggregate/object (stream #\{ #\})
|
||||
(loop for (key . value) in object
|
||||
do (with-element-output ()
|
||||
(encode-assoc-key/value key value stream)))
|
||||
object)
|
||||
;; We can't call *LIST-ENCODER* again, that would be an unlimited recursion
|
||||
(encode-plain-list-to-array object stream)))
|
||||
|
||||
(defun encode-plist (object &optional (stream *standard-output*))
|
||||
(with-aggregate/object (stream #\{ #\})
|
||||
(loop for (key value) on object by #'cddr
|
||||
do (with-element-output ()
|
||||
(encode-assoc-key/value key value stream)))
|
||||
object))
|
||||
|
||||
(defmethod encode ((object (eql 'true)) &optional (stream *standard-output*))
|
||||
(write-string "true" stream)
|
||||
object)
|
||||
|
||||
(defmethod encode ((object (eql 'false)) &optional (stream *standard-output*))
|
||||
(write-string "false" stream)
|
||||
object)
|
||||
|
||||
(defmethod encode ((object (eql :null)) &optional (stream *standard-output*))
|
||||
(write-string "null" stream)
|
||||
object)
|
||||
|
||||
(defmethod encode ((object (eql t)) &optional (stream *standard-output*))
|
||||
(write-string "true" stream)
|
||||
object)
|
||||
|
||||
(defmethod encode ((object (eql nil)) &optional (stream *standard-output*))
|
||||
(write-string "null" stream)
|
||||
object)
|
||||
|
||||
(defclass json-output-stream (trivial-gray-streams:fundamental-character-output-stream)
|
||||
((output-stream :reader output-stream
|
||||
:initarg :output-stream)
|
||||
(stack :accessor stack
|
||||
:initform nil)
|
||||
(indent :initarg :indent
|
||||
:reader indent
|
||||
:accessor indent%)
|
||||
(indent-string :initform ""
|
||||
:accessor indent-string))
|
||||
(:default-initargs :indent *default-indent*)
|
||||
(:documentation "Objects of this class capture the state of a JSON stream encoder."))
|
||||
|
||||
(defmethod initialize-instance :after ((stream json-output-stream) &key indent)
|
||||
(when (eq indent t)
|
||||
(setf (indent% stream) *default-indent-width*)))
|
||||
|
||||
(defgeneric make-json-output-stream (stream &key indent))
|
||||
|
||||
(defmethod make-json-output-stream (stream &key (indent t))
|
||||
"Create a JSON output stream with indentation enabled."
|
||||
(if indent
|
||||
(make-instance 'json-output-stream :output-stream stream :indent indent)
|
||||
stream))
|
||||
|
||||
(defmethod trivial-gray-streams:stream-write-char ((stream json-output-stream) char)
|
||||
(write-char char (output-stream stream)))
|
||||
|
||||
(defgeneric write-indentation (stream)
|
||||
(:method ((stream t))
|
||||
nil)
|
||||
(:method ((stream json-output-stream))
|
||||
(when (indent stream)
|
||||
(fresh-line (output-stream stream))
|
||||
(write-string (indent-string stream) (output-stream stream)))))
|
||||
|
||||
(defgeneric write-delimiter (char stream)
|
||||
(:method (char stream)
|
||||
(write-char char stream))
|
||||
(:method (char (stream json-output-stream))
|
||||
(write-char char (output-stream stream))))
|
||||
|
||||
(defgeneric change-indentation (stream operator)
|
||||
(:method ((stream t) (operator t))
|
||||
nil)
|
||||
(:method ((stream json-output-stream) operator)
|
||||
(when (indent stream)
|
||||
(setf (indent-string stream) (make-string (funcall operator (length (indent-string stream))
|
||||
(indent stream))
|
||||
:initial-element #\Space)))))
|
||||
|
||||
(defun next-aggregate-element ()
|
||||
(if (car (stack *json-output*))
|
||||
(write-char (car (stack *json-output*)) (output-stream *json-output*))
|
||||
(setf (car (stack *json-output*)) #\,)))
|
||||
|
||||
(defmacro with-output ((stream &rest args &key indent) &body body)
|
||||
(declare (ignore indent))
|
||||
"Set up a JSON streaming encoder context on STREAM, then evaluate BODY."
|
||||
`(let ((*json-output* (make-instance 'json-output-stream :output-stream ,stream ,@args)))
|
||||
,@body))
|
||||
|
||||
(defmacro with-output-to-string* ((&rest args &key indent stream-symbol) &body body)
|
||||
"Set up a JSON streaming encoder context, then evaluate BODY.
|
||||
Return a string with the generated JSON output."
|
||||
(declare (ignore indent))
|
||||
(let ((stream (or stream-symbol (gensym "STREAM"))))
|
||||
(remf args :stream-symbol)
|
||||
`(with-output-to-string (,stream)
|
||||
(with-output (,stream ,@args)
|
||||
,@body))))
|
||||
|
||||
(define-condition no-json-output-context (error)
|
||||
()
|
||||
(:report "No JSON output context is active")
|
||||
(:documentation "This condition is signalled when one of the stream
|
||||
encoding function is used outside the dynamic context of a
|
||||
WITH-OUTPUT or WITH-OUTPUT-TO-STRING* body."))
|
||||
|
||||
(defmacro with-aggregate/stream ((begin-char end-char) &body body)
|
||||
"Set up context for aggregate serialization for the stream encoder."
|
||||
`(progn
|
||||
(unless (boundp '*json-output*)
|
||||
(error 'no-json-output-context))
|
||||
(when (stack *json-output*)
|
||||
(next-aggregate-element))
|
||||
(write-indentation *json-output*)
|
||||
(write-delimiter ,begin-char *json-output*)
|
||||
(change-indentation *json-output* #'+)
|
||||
(push nil (stack *json-output*))
|
||||
(prog1
|
||||
(progn ,@body)
|
||||
(pop (stack *json-output*))
|
||||
(change-indentation *json-output* #'-)
|
||||
(write-indentation *json-output*)
|
||||
(write-delimiter ,end-char *json-output*))))
|
||||
|
||||
(defmacro with-array (() &body body)
|
||||
"Open a JSON array, then run BODY. Inside the body,
|
||||
ENCODE-ARRAY-ELEMENT must be called to encode elements to the opened
|
||||
array. Must be called within an existing JSON encoder context, see
|
||||
WITH-OUTPUT and WITH-OUTPUT-TO-STRING*."
|
||||
`(with-aggregate/stream (#\[ #\]) ,@body))
|
||||
|
||||
(defmacro with-object (() &body body)
|
||||
"Open a JSON object, then run BODY. Inside the body,
|
||||
ENCODE-OBJECT-ELEMENT or WITH-OBJECT-ELEMENT must be called to encode
|
||||
elements to the object. Must be called within an existing JSON
|
||||
encoder context, see WITH-OUTPUT and WITH-OUTPUT-TO-STRING*."
|
||||
`(with-aggregate/stream (#\{ #\}) ,@body))
|
||||
|
||||
(defun encode-array-element (object)
|
||||
"Encode OBJECT as next array element to the last JSON array opened
|
||||
with WITH-ARRAY in the dynamic context. OBJECT is encoded using the
|
||||
ENCODE generic function, so it must be of a type for which an ENCODE
|
||||
method is defined."
|
||||
(next-aggregate-element)
|
||||
(write-indentation *json-output*)
|
||||
(encode object (output-stream *json-output*)))
|
||||
|
||||
(defun encode-array-elements (&rest objects)
|
||||
"Encode OBJECTS, a list of JSON encodable objects, as array elements."
|
||||
(dolist (object objects)
|
||||
(encode-array-element object)))
|
||||
|
||||
(defun encode-object-element (key value)
|
||||
"Encode KEY and VALUE as object element to the last JSON object
|
||||
opened with WITH-OBJECT in the dynamic context. KEY and VALUE are
|
||||
encoded using the ENCODE generic function, so they both must be of a
|
||||
type for which an ENCODE method is defined."
|
||||
(next-aggregate-element)
|
||||
(write-indentation *json-output*)
|
||||
(encode-key/value key value (output-stream *json-output*))
|
||||
value)
|
||||
|
||||
(defun encode-object-elements (&rest elements)
|
||||
"Encode plist ELEMENTS as object elements."
|
||||
(loop for (key value) on elements by #'cddr
|
||||
do (encode-object-element key value)))
|
||||
|
||||
(defun encode-object-slots (object slots)
|
||||
"For each slot in SLOTS, encode that slot on OBJECT as an object element.
|
||||
Equivalent to calling ENCODE-OBJECT-ELEMENT for each slot where the
|
||||
key is the slot name, and the value is the (SLOT-VALUE OBJECT slot)"
|
||||
(loop for slot in slots
|
||||
do (encode-object-element (string slot)
|
||||
(slot-value object slot))))
|
||||
|
||||
(define-compiler-macro encode-object-slots (&whole form &environment env object raw-slots)
|
||||
"Compiler macro to allow open-coding with encode-object-slots when slots are literal list."
|
||||
(let ((slots (macroexpand raw-slots env)))
|
||||
(cond
|
||||
((null slots) nil)
|
||||
((eq (car slots) 'quote)
|
||||
(setf slots (cadr slots)) ; Get the quoted list
|
||||
`(with-slots ,slots ,object
|
||||
,@(loop for slot in slots
|
||||
collect `(encode-object-element ,(string slot) ,slot))))
|
||||
(t form))))
|
||||
|
||||
(defmacro with-object-element ((key) &body body)
|
||||
"Open a new encoding context to encode a JSON object element. KEY
|
||||
is the key of the element. The value will be whatever BODY
|
||||
serializes to the current JSON output context using one of the
|
||||
stream encoding functions. This can be used to stream out nested
|
||||
object structures."
|
||||
`(progn
|
||||
(next-aggregate-element)
|
||||
(write-indentation *json-output*)
|
||||
(encode ,key (output-stream *json-output*))
|
||||
(setf (car (stack *json-output*)) #\:)
|
||||
(unwind-protect
|
||||
(progn ,@body)
|
||||
(setf (car (stack *json-output*)) #\,))))
|
||||
|
||||
(defgeneric encode-slots (object)
|
||||
(:documentation
|
||||
"Generic function to encode object slots. 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.")
|
||||
(:method-combination progn :most-specific-last))
|
||||
|
||||
(defgeneric encode-object (object)
|
||||
(:documentation
|
||||
"Generic function to encode an object. The default implementation
|
||||
opens a new object encoding context and calls ENCODE-SLOTS on
|
||||
the argument.")
|
||||
(:method (object)
|
||||
(with-object ()
|
||||
(yason:encode-slots object))))
|
||||
764
sbcl/.quicklisp/dists/quicklisp/software/yason-v0.7.8/index.html
Normal file
764
sbcl/.quicklisp/dists/quicklisp/software/yason-v0.7.8/index.html
Normal file
|
|
@ -0,0 +1,764 @@
|
|||
<!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"></meta><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; }
|
||||
blockquote { margin-left: 2em; margin-right: 2em; }
|
||||
pre { padding:5px; background-color:#e0e0e0 }
|
||||
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; }
|
||||
</style></head><body>
|
||||
|
||||
<h1 xmlns="">YASON - A JSON encoder/decoder for Common Lisp</h1>
|
||||
|
||||
|
||||
|
||||
<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 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">Download and Installation</a></li>
|
||||
<li><a href="#json-package">Using JSON as package name</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>
|
||||
<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>
|
||||
|
||||
|
||||
<h3 xmlns=""><a class="none" name="install">Download and Installation</a></h3>
|
||||
<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 0.7.6.
|
||||
</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>hans.huebner@gmail.com</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>
|
||||
|
||||
|
||||
<h3 xmlns=""><a class="none" name="json-package">Using JSON as package name</a></h3>
|
||||
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>
|
||||
|
||||
|
||||
<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>). Set
|
||||
<code xmlns=""><a href="#*parse-object-as*">*parse-object-as*</a></code> 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
|
||||
<code xmlns=""><a href="#*parse-object-key-fn*">*parse-object-key-fn*</a></code> 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
|
||||
<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 (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>
|
||||
|
||||
<h4 xmlns=""><a name="parser-dict">Parser dictionary</a></h4>
|
||||
<p xmlns="">[Function]<br><a class="none" name="parse"><b>parse</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">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></i>
|
||||
=>
|
||||
<i>object</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
Parse <code><i>input</i></code>, which must be a string or
|
||||
a stream, as JSON. Returns the Lisp representation of the
|
||||
JSON structure parsed.
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">
|
||||
The keyword arguments <code xmlns=""><i>object-key-fn</i></code>,
|
||||
<code xmlns=""><i>object-as</i></code>,
|
||||
<code xmlns=""><i>json-arrays-as-vectors</i></code>,
|
||||
<code xmlns=""><i>json-booleans-as-symbols</i></code>, and
|
||||
<code xmlns=""><i>json-null-as-keyword</i></code> may be used
|
||||
to specify different values for the parsing parameters
|
||||
from the current bindings of the respective special
|
||||
variables.
|
||||
</p>
|
||||
</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">
|
||||
If set to a true value, JSON arrays will be parsed as
|
||||
vectors, not as lists. NIL is the default.
|
||||
</clix:description></blockquote></p>
|
||||
|
||||
<p xmlns="">
|
||||
[Special variable]<br><a class="none" name="*parse-object-as*"><b>*parse-object-as*</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
Can be set to <b xmlns="http://www.w3.org/1999/xhtml">:hash-table</b> to parse objects as hash
|
||||
tables, <b xmlns="http://www.w3.org/1999/xhtml">:alist</b> to parse them as alists or
|
||||
<b xmlns="http://www.w3.org/1999/xhtml">:plist</b> to parse them as plists. <b xmlns="http://www.w3.org/1999/xhtml">:hash-table</b>
|
||||
is the default.
|
||||
</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">
|
||||
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></blockquote></p>
|
||||
|
||||
<p xmlns="">
|
||||
[Special variable]<br><a class="none" name="*parse-json-null-as-keyword*"><b>*parse-json-null-as-keyword*</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
If set to a true value, JSON null will be read as the
|
||||
keyword :NULL, instead of NIL.
|
||||
NIL is the default.
|
||||
</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">
|
||||
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></blockquote></p>
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 xmlns=""><a class="none" name="encoding">Encoding JSON data</a></h3>
|
||||
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 serialze 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
|
||||
<code xmlns=""><a href="#json-output-stream">JSON-OUTPUT-STREAM</a></code> 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
|
||||
<code xmlns=""><a href="#make-json-output-stream">MAKE-JSON-OUTPUT-STREAM</a></code> 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>
|
||||
|
||||
<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> (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>
|
||||
|
||||
<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">
|
||||
Encode <code><i>object</i></code> in JSON format and
|
||||
write to <code><i>stream</i></code>. May be specialized
|
||||
by applications to perform specific rendering. Stream
|
||||
defaults to *STANDARD-OUTPUT*.
|
||||
</clix:description></blockquote></p>
|
||||
|
||||
<p xmlns="">[Function]<br><a class="none" name="encode-alist"><b>encode-alist</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">object &optional (stream
|
||||
*standard-output*)</clix:lambda-list></i>
|
||||
=>
|
||||
<i>object</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
Encodes <code><i>object</i></code>, an alist, in JSON
|
||||
format and write to <code><i>stream</i></code>.
|
||||
</clix:description></blockquote></p>
|
||||
|
||||
<p xmlns="">[Function]<br><a class="none" name="encode-plist"><b>encode-plist</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">object &optional (stream
|
||||
*standard-output*)</clix:lambda-list></i>
|
||||
=>
|
||||
<i>object</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
Encodes <code><i>object</i></code>, a plist, in JSON
|
||||
format and write to <code><i>stream</i></code>.
|
||||
</clix:description></blockquote></p>
|
||||
|
||||
<p xmlns="">[Function]<br><a class="none" name="make-json-output-stream"><b>make-json-output-stream</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">stream &key (indent t)</clix:lambda-list></i>
|
||||
=>
|
||||
<i>stream</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
Creates a <code><a href="#json-output-stream">json-output-stream</a></code> instance
|
||||
that wraps the supplied <code><i>stream</i></code> and
|
||||
optionally performs indentation of the generated JSON
|
||||
data. The <code><i>indent</i></code> argument is
|
||||
described in <code><a href="#with-output">WITH-OUTPUT</a></code>. Note that
|
||||
if the <code><i>indent</i></code> argument is NIL, the
|
||||
original stream is returned in order to avoid the
|
||||
performance penalty of the indentation algorithm.
|
||||
</clix:description></blockquote></p>
|
||||
|
||||
<p xmlns="">
|
||||
[Special variable]<br><a class="none" name="*list-encoder*"><b>*list-encoder*</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
Function to call to translate a CL list into JSON data.
|
||||
<code><i>'YASON:ENCODE-PLAIN-LIST-TO-ARRAY</i></code> is the default;
|
||||
<code><i>'YASON:ENCODE-PLIST</i></code> and
|
||||
<code><i>'YASON:ENCODE-ALIST</i></code> are available to produce
|
||||
JSON objects.
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">
|
||||
This is useful to translate a deeply recursive structure in a single
|
||||
<code xmlns=""><i>YASON:ENCODE</i></code> call.
|
||||
</p>
|
||||
</clix:description></blockquote></p>
|
||||
|
||||
|
||||
<p xmlns="">
|
||||
[Special variable]<br><a class="none" name="*symbol-key-encoder*"><b>*symbol-key-encoder*</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
Defines the policy to encode symbols as keys (eg. in hash tables).
|
||||
The default is to error out, to provide backwards-compatible behaviour.
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">
|
||||
A useful function that can be bound to this variable is
|
||||
<code xmlns=""><a href="#yason:encode-symbol-as-lowercase.">YASON:ENCODE-SYMBOL-AS-LOWERCASE.</a></code>
|
||||
</p>
|
||||
</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 data structure. 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> (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>
|
||||
|
||||
<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 &key indent) &body body</clix:lambda-list></i>
|
||||
=>
|
||||
<i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
Set up a JSON streaming encoder context on
|
||||
<code><i>stream</i></code>, then evaluate
|
||||
<code><i>body</i></code>. <code><i>indent</i></code>
|
||||
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></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">(&key indent stream-symbol) &body body</clix:lambda-list></i>
|
||||
=>
|
||||
<i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
Set up a JSON streaming encoder context on
|
||||
<code><i>stream-symbol</i></code> (by default
|
||||
a gensym), then evaluate
|
||||
<code><i>body</i></code>. Return a string with the
|
||||
generated JSON output. See
|
||||
<code><a href="#with-output">WITH-OUTPUT</a></code> for the description of
|
||||
the <code><i>indent</i></code> keyword argument.
|
||||
</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">
|
||||
This condition is signalled when one of the stream
|
||||
encoding functions 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">
|
||||
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">
|
||||
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="">[Function]<br><a class="none" name="encode-array-elements"><b>encode-array-elements</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">&rest objects</clix:lambda-list></i>
|
||||
=>
|
||||
<i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
Encode <code><i>objects</i></code>, a series of JSON
|
||||
encodable objects, as the next array elements in a JSON
|
||||
array opened with
|
||||
<code><a href="#with-array">WITH-ARRAY</a></code>. ENCODE-ARRAY-ELEMENTS
|
||||
uses <code><a href="#encode-array-element">ENCODE-ARRAY-ELEMENT</a></code>, which must
|
||||
be applicable to each object in the list
|
||||
(i.e. <code><a href="#encode">ENCODE</a></code> must be defined for each
|
||||
object type). Additionally, this must be called within a
|
||||
valid stream context.
|
||||
</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">
|
||||
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
|
||||
<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">
|
||||
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">
|
||||
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>
|
||||
|
||||
<p xmlns="">[Function]<br><a class="none" name="encode-object-elements"><b>encode-object-elements</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">&rest elements</clix:lambda-list></i>
|
||||
=>
|
||||
<i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
Encodes the parameters into JSON in the last object opened
|
||||
with <code><a href="#with-object">WITH-OBJECT</a></code> using
|
||||
<code><a href="#encode-object-element">ENCODE-OBJECT-ELEMENT</a></code>. The parameters
|
||||
should consist of alternating key/value pairs, and this
|
||||
must be called within a valid stream context.
|
||||
</clix:description></blockquote></p>
|
||||
|
||||
<p xmlns="">[Function]<br><a class="none" name="encode-object-slots"><b>encode-object-slots</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">object slots</clix:lambda-list></i>
|
||||
=>
|
||||
<i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
Encodes each slot in SLOTS for OBJECT in the last object
|
||||
opened with <code><a href="#with-object">WITH-OBJECT</a></code> using
|
||||
<code><a href="#encode-object-element">ENCODE-OBJECT-ELEMENT</a></code>. The key is the
|
||||
slot name, and the value is the slot value for the slot on
|
||||
OBJECT. It is equivalent to
|
||||
<pre xmlns="http://www.w3.org/1999/xhtml">(loop for slot in slots
|
||||
do (encode-object-element (string slot)
|
||||
(slot-value object slot)))
|
||||
</pre>
|
||||
</clix:description></blockquote></p>
|
||||
|
||||
<p xmlns="">[Function]<br><a class="none" name="encode-slots"><b>encode-slots</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">object</clix:lambda-list></i>
|
||||
=>
|
||||
<i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
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></blockquote></p>
|
||||
|
||||
<p xmlns="">[Function]<br><a class="none" name="encode-object"><b>encode-object</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">object</clix:lambda-list></i>
|
||||
=>
|
||||
<i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
Generic function to encode an object. The default implementation
|
||||
opens a new object encoding context and calls
|
||||
<code><a href="#encode-slots">ENCODE-SLOTS</a></code> on the argument.
|
||||
</clix:description></blockquote></p>
|
||||
|
||||
<p xmlns="">
|
||||
[Standard class]<br><a class="none" name="json-output-stream"><b>json-output-stream</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
|
||||
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
|
||||
<code><a href="#make-json-output-stream">make-json-output-stream</a></code> to create a
|
||||
wrapper stream if required.
|
||||
</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 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. <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.
|
||||
|
||||
<p>For an example of the interplay between
|
||||
<code xmlns=""><a href="#encode-object">ENCODE-OBJECT</a></code> and
|
||||
<code xmlns=""><a href="#encode-slots">ENCODE-SLOTS</a></code>, 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
|
||||
<code xmlns=""><a href="#encode-slots">ENCODE-SLOTS</a></code> 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 <code xmlns=""><a href="#encode-object">ENCODE-OBJECT</a></code>:
|
||||
|
||||
<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 <code xmlns=""><a href="#encode-object-slots">ENCODE-OBJECT-SLOTS</a></code>
|
||||
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>
|
||||
|
||||
|
||||
|
||||
<h3 xmlns=""><a class="none" name="index">Symbol index</a></h3>
|
||||
<ul xmlns="">
|
||||
<li><code><a href="#*list-encoder*">*list-encoder*</a></code></li>
|
||||
<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-json-null-as-keyword*">*parse-json-null-as-keyword*</a></code></li>
|
||||
<li><code><a href="#*parse-object-as*">*parse-object-as*</a></code></li>
|
||||
<li><code><a href="#*parse-object-key-fn*">*parse-object-key-fn*</a></code></li>
|
||||
<li><code><a href="#*symbol-key-encoder*">*symbol-key-encoder*</a></code></li>
|
||||
<li><code><a href="#encode">encode</a></code></li>
|
||||
<li><code><a href="#encode-alist">encode-alist</a></code></li>
|
||||
<li><code><a href="#encode-array-element">encode-array-element</a></code></li>
|
||||
<li><code><a href="#encode-array-elements">encode-array-elements</a></code></li>
|
||||
<li><code><a href="#encode-object">encode-object</a></code></li>
|
||||
<li><code><a href="#encode-object-element">encode-object-element</a></code></li>
|
||||
<li><code><a href="#encode-object-elements">encode-object-elements</a></code></li>
|
||||
<li><code><a href="#encode-object-slots">encode-object-slots</a></code></li>
|
||||
<li><code><a href="#encode-plist">encode-plist</a></code></li>
|
||||
<li><code><a href="#encode-slots">encode-slots</a></code></li>
|
||||
<li><code><a href="#json-output-stream">json-output-stream</a></code></li>
|
||||
<li><code><a href="#make-json-output-stream">make-json-output-stream</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-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>
|
||||
|
||||
|
||||
<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. Thanks to David Lichteblau for coining YASON's name.
|
||||
|
||||
|
||||
</body></html>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
;; This file is part of yason, a Common Lisp JSON parser/encoder
|
||||
;;
|
||||
;; Copyright (c) 2008-2019 Hans Huebner and contributors
|
||||
;; All rights reserved.
|
||||
;;
|
||||
;; Please see the file LICENSE in the distribution.
|
||||
|
||||
(defpackage :yason
|
||||
|
||||
(:use :cl)
|
||||
|
||||
(:export
|
||||
;; Parser
|
||||
#:parse
|
||||
#:*parse-object-key-fn*
|
||||
#:*parse-object-as*
|
||||
#:*parse-object-as-alist* ; deprecated
|
||||
#:*parse-json-arrays-as-vectors*
|
||||
#:*parse-json-booleans-as-symbols*
|
||||
#:*parse-json-null-as-keyword*
|
||||
|
||||
#:true
|
||||
#:false
|
||||
#:null
|
||||
|
||||
;; Basic encoder interface
|
||||
#:encode
|
||||
#:encode-slots
|
||||
#:encode-object
|
||||
#:encode-plist
|
||||
#:encode-alist
|
||||
#:encode-plain-list-to-array
|
||||
#:*list-encoder*
|
||||
#:*symbol-key-encoder*
|
||||
#:encode-symbol-as-lowercase
|
||||
|
||||
#:make-json-output-stream
|
||||
|
||||
;; Streaming encoder interface
|
||||
#:with-output
|
||||
#:with-output-to-string*
|
||||
#:no-json-output-context
|
||||
#:with-array
|
||||
#:encode-array-element
|
||||
#:encode-array-elements
|
||||
#:with-object
|
||||
#:encode-object-element
|
||||
#:encode-object-elements
|
||||
#:encode-object-slots
|
||||
#:with-object-element))
|
||||
258
sbcl/.quicklisp/dists/quicklisp/software/yason-v0.7.8/parse.lisp
Normal file
258
sbcl/.quicklisp/dists/quicklisp/software/yason-v0.7.8/parse.lisp
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
;; This file is part of yason, a Common Lisp JSON parser/encoder
|
||||
;;
|
||||
;; Copyright (c) 2008-2014 Hans Huebner and contributors
|
||||
;; All rights reserved.
|
||||
;;
|
||||
;; Please see the file LICENSE in the distribution.
|
||||
|
||||
(in-package :yason)
|
||||
|
||||
(defconstant +default-string-length+ 20
|
||||
"Default length of strings that are created while reading json input.")
|
||||
|
||||
(defvar *parse-object-key-fn* #'identity
|
||||
"Function to call to convert a key string in a JSON array to a key
|
||||
in the CL hash produced.")
|
||||
|
||||
(defvar *parse-json-arrays-as-vectors* nil
|
||||
"If set to a true value, JSON arrays will be parsed as vectors, not
|
||||
as lists.")
|
||||
|
||||
(defvar *parse-json-booleans-as-symbols* nil
|
||||
"If set to a true value, JSON booleans will be read as the symbols
|
||||
TRUE and FALSE, not as T and NIL, respectively.")
|
||||
|
||||
(defvar *parse-json-null-as-keyword* nil
|
||||
"If set to a true value, JSON nulls will be read as the keyword :NULL, not as NIL.")
|
||||
|
||||
(defvar *parse-object-as* :hash-table
|
||||
"Set to either :hash-table, :plist or :alist to determine the data
|
||||
structure that objects are parsed to.")
|
||||
|
||||
(defvar *parse-object-as-alist* nil
|
||||
"DEPRECATED, provided for backward compatibility")
|
||||
|
||||
(defun make-adjustable-string ()
|
||||
"Return an adjustable empty string, usable as a buffer for parsing strings and numbers."
|
||||
(make-array +default-string-length+
|
||||
:adjustable t :fill-pointer 0 :element-type 'character))
|
||||
|
||||
(defun parse-number (input)
|
||||
;; would be
|
||||
;; (cl-ppcre:scan-to-strings "^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+|)(?:[eE][-+]?[0-9]+|)" buffer)
|
||||
;; but we want to operate on streams
|
||||
(let ((buffer (make-adjustable-string)))
|
||||
(loop while (position (peek-char nil input nil) ".0123456789+-Ee")
|
||||
do (vector-push-extend (read-char input) buffer))
|
||||
(values (read-from-string buffer))))
|
||||
|
||||
(defun parse-unicode-escape (input)
|
||||
(let ((char-code (let ((buffer (make-string 4)))
|
||||
(read-sequence buffer input)
|
||||
(parse-integer buffer :radix 16))))
|
||||
(if (and (>= char-code #xd800)
|
||||
(<= char-code #xdbff))
|
||||
(let ((buffer (make-string 6)))
|
||||
(read-sequence buffer input)
|
||||
(when (not (string= buffer "\\u" :end1 2))
|
||||
(error "Lead Surrogate without Tail Surrogate"))
|
||||
(let ((tail-code (parse-integer buffer :radix 16 :start 2)))
|
||||
(when (not (and (>= tail-code #xdc00)
|
||||
(<= tail-code #xdfff)))
|
||||
(error "Lead Surrogate without Tail Surrogate"))
|
||||
(code-char (+ #x010000
|
||||
(ash (- char-code #xd800) 10)
|
||||
(- tail-code #xdc00)))))
|
||||
(code-char char-code))))
|
||||
|
||||
(defun parse-string (input)
|
||||
(let ((output (make-adjustable-string)))
|
||||
(labels ((outc (c)
|
||||
(vector-push-extend c output))
|
||||
(next ()
|
||||
(read-char input))
|
||||
(peek ()
|
||||
(peek-char nil input)))
|
||||
(let* ((starting-symbol (next))
|
||||
(string-quoted (equal starting-symbol #\")))
|
||||
(unless string-quoted
|
||||
(outc starting-symbol))
|
||||
(loop
|
||||
(cond
|
||||
((eql (peek) #\")
|
||||
(next)
|
||||
(return-from parse-string output))
|
||||
((eql (peek) #\\)
|
||||
(next)
|
||||
(ecase (next)
|
||||
(#\" (outc #\"))
|
||||
(#\\ (outc #\\))
|
||||
(#\/ (outc #\/))
|
||||
(#\b (outc #\Backspace))
|
||||
(#\f (outc #\Page))
|
||||
(#\n (outc #\Newline))
|
||||
(#\r (outc #\Return))
|
||||
(#\t (outc #\Tab))
|
||||
(#\u (outc (parse-unicode-escape input)))))
|
||||
((and (or (whitespace-p (peek))
|
||||
(eql (peek) #\:))
|
||||
(not string-quoted))
|
||||
(return-from parse-string output))
|
||||
(t
|
||||
(outc (next)))))))))
|
||||
|
||||
(defun whitespace-p (char)
|
||||
(member char '(#\Space #\Newline #\Tab #\Linefeed #\Return)))
|
||||
|
||||
(defun skip-whitespace (input)
|
||||
(loop for c = (peek-char nil input nil nil)
|
||||
while (and c (whitespace-p c))
|
||||
do (read-char input)))
|
||||
|
||||
(defun peek-char-skipping-whitespace (input &optional (eof-error-p t))
|
||||
(skip-whitespace input)
|
||||
(peek-char nil input eof-error-p))
|
||||
|
||||
(defun parse-constant (input)
|
||||
(destructuring-bind (expected-string return-value)
|
||||
(find (peek-char nil input nil)
|
||||
`(("true" ,(if *parse-json-booleans-as-symbols* 'true t))
|
||||
("false" ,(if *parse-json-booleans-as-symbols* 'false nil))
|
||||
("null" ,(if *parse-json-null-as-keyword* :null nil)))
|
||||
:key (lambda (entry) (aref (car entry) 0))
|
||||
:test #'eql)
|
||||
(loop for char across expected-string
|
||||
unless (eql (read-char input nil) char)
|
||||
do (error "invalid constant"))
|
||||
return-value))
|
||||
|
||||
(define-condition cannot-convert-key (error)
|
||||
((key-string :initarg :key-string
|
||||
:reader key-string))
|
||||
(:report (lambda (c stream)
|
||||
(format stream "cannot convert key ~S used in JSON object to hash table key"
|
||||
(key-string c)))))
|
||||
|
||||
(defun create-container ()
|
||||
(ecase *parse-object-as*
|
||||
((:plist :alist)
|
||||
nil)
|
||||
(:hash-table
|
||||
(make-hash-table :test #'equal))))
|
||||
|
||||
(defun add-attribute (to key value)
|
||||
(ecase *parse-object-as*
|
||||
(:plist
|
||||
(append to (list key value)))
|
||||
(:alist
|
||||
(acons key value to))
|
||||
(:hash-table
|
||||
(setf (gethash key to) value)
|
||||
to)))
|
||||
|
||||
(define-condition expected-colon (error)
|
||||
((key-string :initarg :key-string
|
||||
:reader key-string))
|
||||
(:report (lambda (c stream)
|
||||
(format stream "expected colon to follow key ~S used in JSON object"
|
||||
(key-string c)))))
|
||||
|
||||
(defun parse-object (input)
|
||||
(let ((return-value (create-container)))
|
||||
(read-char input)
|
||||
(loop
|
||||
(when (eql (peek-char-skipping-whitespace input)
|
||||
#\})
|
||||
(return))
|
||||
(skip-whitespace input)
|
||||
(setf return-value
|
||||
(add-attribute return-value
|
||||
(let ((key-string (parse-string input)))
|
||||
(prog1
|
||||
(or (funcall *parse-object-key-fn* key-string)
|
||||
(error 'cannot-convert-key :key-string key-string))
|
||||
(skip-whitespace input)
|
||||
(unless (eql #\: (read-char input))
|
||||
(error 'expected-colon :key-string key-string))
|
||||
(skip-whitespace input)))
|
||||
(parse input)))
|
||||
(ecase (peek-char-skipping-whitespace input)
|
||||
(#\, (read-char input))
|
||||
(#\} nil)))
|
||||
(read-char input)
|
||||
return-value))
|
||||
|
||||
(defconstant +initial-array-size+ 20
|
||||
"Initial size of JSON arrays read, they will grow as needed.")
|
||||
|
||||
(defun %parse-array (input add-element-function)
|
||||
"Parse JSON array from input, calling ADD-ELEMENT-FUNCTION for each array element parsed."
|
||||
(read-char input)
|
||||
(loop
|
||||
(when (eql (peek-char-skipping-whitespace input)
|
||||
#\])
|
||||
(return))
|
||||
(funcall add-element-function (parse input))
|
||||
(ecase (peek-char-skipping-whitespace input)
|
||||
(#\, (read-char input))
|
||||
(#\] nil)))
|
||||
(read-char input))
|
||||
|
||||
(defun parse-array (input)
|
||||
(if *parse-json-arrays-as-vectors*
|
||||
(let ((return-value (make-array +initial-array-size+ :adjustable t :fill-pointer 0)))
|
||||
(%parse-array input
|
||||
(lambda (element)
|
||||
(vector-push-extend element return-value)))
|
||||
return-value)
|
||||
(let (return-value)
|
||||
(%parse-array input
|
||||
(lambda (element)
|
||||
(push element return-value)))
|
||||
(nreverse return-value))))
|
||||
|
||||
(defgeneric parse% (input)
|
||||
(:method ((input stream))
|
||||
;; backward compatibility code
|
||||
(assert (or (not *parse-object-as-alist*)
|
||||
(eq *parse-object-as* :hash-table))
|
||||
() "unexpected combination of *parse-object-as* and *parse-object-as-alist*, please use *parse-object-as* exclusively")
|
||||
(let ((*parse-object-as* (if *parse-object-as-alist*
|
||||
:alist
|
||||
*parse-object-as*)))
|
||||
;; end of backward compatibility code
|
||||
(check-type *parse-object-as* (member :hash-table :alist :plist))
|
||||
(ecase (peek-char-skipping-whitespace input)
|
||||
(#\"
|
||||
(parse-string input))
|
||||
((#\- #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
|
||||
(parse-number input))
|
||||
(#\{
|
||||
(parse-object input))
|
||||
(#\[
|
||||
(parse-array input))
|
||||
((#\t #\f #\n)
|
||||
(parse-constant input)))))
|
||||
(:method ((input pathname))
|
||||
(with-open-file (stream input)
|
||||
(parse stream)))
|
||||
(:method ((input string))
|
||||
(parse (make-string-input-stream input))))
|
||||
|
||||
(defun parse (input
|
||||
&key
|
||||
(object-key-fn *parse-object-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*))
|
||||
"Parse INPUT, which needs to be a string or a stream, as JSON.
|
||||
Returns the lisp representation of the JSON structure parsed. The
|
||||
keyword arguments can be used to override the parser settings as
|
||||
defined by the respective special variables."
|
||||
(let ((*parse-object-key-fn* object-key-fn)
|
||||
(*parse-object-as* object-as)
|
||||
(*parse-json-arrays-as-vectors* json-arrays-as-vectors)
|
||||
(*parse-json-booleans-as-symbols* json-booleans-as-symbols)
|
||||
(*parse-json-null-as-keyword* json-nulls-as-keyword))
|
||||
(parse% input)))
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
|
||||
xmllint --noout doc.xml
|
||||
[ -f clixdoc.xsl ] || wget -q https://raw.github.com/hanshuebner/clixdoc/master/clixdoc.xsl
|
||||
xsltproc --stringparam current-release `perl -ne 'if (/^ *:version +"(.*)"/) { print "$1\n" }' yason.asd` -o index.html clixdoc.xsl doc.xml
|
||||
116
sbcl/.quicklisp/dists/quicklisp/software/yason-v0.7.8/test.lisp
Normal file
116
sbcl/.quicklisp/dists/quicklisp/software/yason-v0.7.8/test.lisp
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
(defpackage :yason-test
|
||||
(:use :cl :unit-test))
|
||||
|
||||
(in-package :yason-test)
|
||||
|
||||
(defparameter *basic-test-json-string* "[{\"foo\":1,\"bar\":[7,8,9]},2,3,4,[5,6,7],true,null]")
|
||||
(defparameter *basic-test-json-string-indented* "
|
||||
[
|
||||
{\"foo\":1,
|
||||
\"bar\":[7,8,9]
|
||||
},
|
||||
2, 3, 4, [5, 6, 7], true, null
|
||||
]")
|
||||
(defparameter *basic-test-json-dom* (list (alexandria:plist-hash-table
|
||||
'("foo" 1 "bar" (7 8 9))
|
||||
:test #'equal)
|
||||
2 3 4
|
||||
'(5 6 7)
|
||||
t nil))
|
||||
|
||||
|
||||
(deftest :yason "parser.basic"
|
||||
(let ((result (yason:parse *basic-test-json-string*)))
|
||||
(test-equal (first *basic-test-json-dom*) (first result) :test #'equalp)
|
||||
(test-equal (rest *basic-test-json-dom*) (rest result))))
|
||||
|
||||
(deftest :yason "parser.basic-with-whitespace"
|
||||
(let ((result (yason:parse *basic-test-json-string-indented*)))
|
||||
(test-equal (first *basic-test-json-dom*) (first result) :test #'equalp)
|
||||
(test-equal (rest *basic-test-json-dom*) (rest result))))
|
||||
|
||||
(deftest :yason "dom-encoder.basic"
|
||||
(let ((result (yason:parse
|
||||
(with-output-to-string (s)
|
||||
(yason:encode *basic-test-json-dom* s)))))
|
||||
(test-equal (first *basic-test-json-dom*) (first result) :test #'equalp)
|
||||
(test-equal (rest *basic-test-json-dom*) (rest result))))
|
||||
|
||||
(defun whitespace-char-p (char)
|
||||
(member char '(#\space #\tab #\return #\newline #\linefeed)))
|
||||
|
||||
(deftest :yason "dom-encoder.indentation"
|
||||
(test-equal "[
|
||||
1,
|
||||
2,
|
||||
3
|
||||
]"
|
||||
(with-output-to-string (s)
|
||||
(yason:encode '(1 2 3) (yason:make-json-output-stream s :indent 10))))
|
||||
(dolist (indentation-arg '(nil t 2 20))
|
||||
(test-equal "[1,2,3]" (remove-if #'whitespace-char-p
|
||||
(with-output-to-string (s)
|
||||
(yason:encode '(1 2 3)
|
||||
(yason:make-json-output-stream s :indent indentation-arg)))))))
|
||||
|
||||
(deftest :yason "stream-encoder.basic-array"
|
||||
(test-equal "[0,1,2]"
|
||||
(with-output-to-string (s)
|
||||
(yason:with-output (s)
|
||||
(yason:with-array ()
|
||||
(dotimes (i 3)
|
||||
(yason:encode-array-element i)))))))
|
||||
|
||||
(deftest :yason "stream-encoder.basic-object"
|
||||
(test-equal "{\"hello\":\"hu hu\",\"harr\":[0,1,2]}"
|
||||
(with-output-to-string (s)
|
||||
(yason:with-output (s)
|
||||
(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)))))))))
|
||||
|
||||
(deftest :yason "stream-encode.unicode-string"
|
||||
(test-equal "\"ab\\u0002 cde \\uD834\\uDD1E\""
|
||||
(with-output-to-string (s)
|
||||
(yason:encode (format nil "ab~C cde ~C" (code-char #x02) (code-char #x1d11e)) s))))
|
||||
|
||||
(defstruct user name age password)
|
||||
|
||||
(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)))))
|
||||
|
||||
(deftest :yason "stream-encoder.application-struct"
|
||||
(test-equal "[{\"name\":\"horst\",\"age\":27},{\"name\":\"uschi\",\"age\":28}]"
|
||||
(with-output-to-string (s)
|
||||
(yason:encode (list (make-user :name "horst" :age 27 :password "puppy")
|
||||
(make-user :name "uschi" :age 28 :password "kitten"))
|
||||
s))))
|
||||
|
||||
(deftest :yason "recursive-alist-encode"
|
||||
(test-equal "{\"a\":3,\"b\":[1,2,{\"c\":4,\"d\":[6]}]}"
|
||||
(yason:with-output-to-string* (:stream-symbol s)
|
||||
(let ((yason:*list-encoder* #'yason:encode-alist))
|
||||
(yason:encode
|
||||
`(("a" . 3) ("b" . #(1 2 (("c" . 4) ("d" . #(6))))))
|
||||
s)))))
|
||||
|
||||
(deftest :yason "symbols-as-keys"
|
||||
(test-condition
|
||||
(yason:with-output-to-string* (:stream-symbol s)
|
||||
(let ((yason:*symbol-key-encoder* #'yason:encode-symbol-as-lowercase))
|
||||
(yason:encode-alist
|
||||
`((:|abC| . 3))
|
||||
s)))
|
||||
'error)
|
||||
(test-equal "{\"a\":3}"
|
||||
(yason:with-output-to-string* (:stream-symbol s)
|
||||
(let ((yason:*symbol-key-encoder* #'yason:encode-symbol-as-lowercase))
|
||||
(yason:encode-alist
|
||||
`((:a . 3))
|
||||
s)))))
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
;;;; -*- Mode: Lisp -*-
|
||||
|
||||
;; This file is part of yason, a Common Lisp JSON parser/encoder
|
||||
;;
|
||||
;; Copyright (c) 2008-2014 Hans Huebner and contributors
|
||||
;; All rights reserved.
|
||||
;;
|
||||
;; Please see the file LICENSE in the distribution.
|
||||
|
||||
(in-package :cl-user)
|
||||
|
||||
(defpackage :yason.system
|
||||
(:use :cl :asdf))
|
||||
|
||||
(in-package :yason.system)
|
||||
|
||||
(defsystem :yason
|
||||
:name "YASON"
|
||||
:author "Hans Huebner <hans@huebner.org>"
|
||||
:version "0.7.6"
|
||||
:maintainer "Hans Huebner <hans@huebner.org>"
|
||||
:licence "BSD"
|
||||
:description "JSON parser/encoder"
|
||||
:long-description "YASON is a Common Lisp library for encoding and
|
||||
decoding data in the JSON 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."
|
||||
|
||||
:depends-on (:alexandria :trivial-gray-streams)
|
||||
:components ((:file "package")
|
||||
(:file "encode" :depends-on ("package"))
|
||||
(:file "parse" :depends-on ("package"))))
|
||||
Loading…
Add table
Add a link
Reference in a new issue