CL-RemiMarshal
CL-RemiMarshal
is a library for Common Lisp that allows automatic
marshalling/serializing of JSON and YAML data to and from CLOS classes. It
places emphasis on speed, and also ensuring the data is properly typed.
CL-RemiMarshal is entirely written and maintained by one person, Remilia Scarlet! If you want to support her and CL-RemiMarshal, you can buy her a coffee on Ko-Fi, or support her through Liberapay. Support is greatly appreciated for this volunteer effort ^_^
How do I get set up?
Most dependencies can be installed via Quicklisp. Check the .asd file to see what you'll need. The CL-SDM and CL-RemiYaml dependencies cannot, however, be installed from Quicklisp at the time of writing. This can instead be obtained from:
Once you have CL-SDM and CL-RemiYaml, put it (and this library) somewhere where ASDF can find them, then run this in a REPL:
(asdf:load-system :cl-remimarshal)
Usage
CL-RemiMarshal is based around metaclasses, which allow you to use some additional slot options to define how to marshal/unmarshal (serialize/deserialize) data using CLOS classes. It also emphasizes type checking to better ensure data integrity and code correctness.
The package nicknames cl-rm:
, cl-rm/json:
, cl-rm/yaml:
, and cl-rm/utils:
are all defined for shorter typing.
(asdf:load-system :cl-remimarshal)
(defclass data-config ()
((store-data?
:initarg :store-data?
:initform nil
:marshal-type :bool
:accessor data-config-store-data-p))
(:metaclass cl-remimarshal-yaml:yaml-unmarshallable))
(defclass config ()
((username
:initarg :username
:initform ""
:marshal-type :string
:accessor config-username)
(hostname
:initarg :hostname
:initform nil
:key-name "host"
:marshal-type :string?
:accessor config-hostname)
(data-config
:initform (make-instance 'data-config)
:key-name "data"
:marshal-type (:class data-config)))
(:metaclass cl-remimarshal-yaml:yaml-unmarshallable))
;; Read a file into a new CONFIG instance.
(cl-remimarshal-yaml:unmarshal #P"/path/to/config.yaml" 'config)
;; Marshal a new instance to a string.
(cl-remimarshal-yaml:marshal-to-string (make-instance 'config))
The :marshal-type
key can take any of the following values. The options with
?
indicate "or null":
:string
,:string?
:bool
,:bool?
:list-bool
,:list-bool?
,:list?-bool
,:list?-bool?
:list-string
,:list-string?
,:list?-string
,:list?-string?
:uint8
,:uint8?
,:uint16
,:uint16?
,:uint24
,:uint24?
,:uint32
:uint32?
,:uint64
,:uint64?
:int8
,:int8?
,:int16
,:int16?
,:int24
,:int24?
,:int32
:int32?
,:int64
,:int64?
:single-float
,:double-float
:list-uint8
,:list-uint8?
,:list-uint16
,:list-uint16?
:list-uint24
,:list-uint24?
,:list-uint32
,:list-uint32?
:list-uint64
,:list-uint64?
:list-int8
,:list-int8?
,:list-int16
,:list-int16?
,:list-int24
:list-int24?
,:list-int32
,:list-int32?
,:list-int64
,:list-int64?
:list-single-float
,:list-double-float
:list?-uint8
,:list?-uint8?
,:list?-uint16
,:list?-uint16?
:list?-uint24
,:list?-uint24?
,:list?-uint32
,:list?-uint32?
:list?-uint64
,:list?-uint64?
:list?-int8
,:list?-int8?
,:list?-int16
,:list?-int16?
:list?-int24?
,:list?-int24?
,:list?-int32
,:list?-int32?
:list?-int64
,:list?-int64?
:list?-single-float
,:list?-double-float
:list?-single-float?
,:list?-double-float?
:any
(store the raw deserialized value, no type checking done).
It is also possible to marhsal/unmarshal data to/from nested CLOS classes. This
is accomplished with the following values. See the files
examples/unmarshal-to-class.lisp
and examples/unmarshal-to-table.lisp
for
information on how to use these.
:class
,:class-list
,:class?
,:class-list?
:class-table
,:class-table?
Lastly, rather than using these pre-defined types, you can also write custom
marshalling/unmarshalling functions. These use the :marshal-fn
and
:unmarshal-fn
slot options, respectively. A large set of predefined ones for
common usage patterns are in the :cl-remimarshal-utils
package. See the files
examples/custom-functions.lisp
and examples/handling-nulls.lisp
for how to
use these.
Development
Style info
I use a slightly unorthodox style for my code. Aside from these differences, please use normal Lisp formatting.
- Keep lines 118 characters or shorter. Obviously sometimes you can't, but please try. Use 115 characters for Markdown files, though.
- I mark types using the form
T/....
. For example,T/SOME-NEAT-TYPE
. For predicates on these, useSOME-NEAT-TYPE-P
. - No tabs. Only spaces.
How do I contribute?
- Go to https://chiselapp.com/user/MistressRemilia/repository/cl-remimarshal and clone the Fossil repository.
- Create a new branch for your feature.
- Push locally to the new branch.
- Create a bundle with Fossil that contains your changes.
- Get in contact with Remilia via email, Fediverse, or open a ticket.
Contributors
- Remilia Scarlet - creator and maintainer
- Homepage: https://remilia.sdf.org/
- Fediverse: @remilia@social.cyberia9.org
- Email: zremiliaz@postzeoz.jpz My real address does not contain Z's
Links and Licenses
CL-RemiMarshal is under the GNU Affero General Public License version 3.