~skeeto/public-inbox

2 2

Re: Options for Structured Data in Emacs Lisp

Michael
Details
Message ID
<87zh0bc2gn.fsf@runbox.com>
DKIM signature
pass
Download raw message
Hey Chris,

Came across this article from a few years back while while
looking to solve a problem of my own: how to serialize &
deserialize CL structs to & from a human-friendly format. Of
course, you can just use `prin1-to-string' but the resulting
format doesn't lend itsself to hand-editing.

Anyone know of a library that would serialize a CL struct to,
say, a property list?

-- 
Michael <sp1ff@runbox.com>

Re: Options for Structured Data in Emacs Lisp

Details
Message ID
<20210210230431.nbp5ejpj6dkosn7h@nullprogram.com>
In-Reply-To
<87zh0bc2gn.fsf@runbox.com> (view parent)
DKIM signature
missing
Download raw message
You can reflect cl-defstruct to convert records to plists. For instance:

(cl-defstruct poi name category latitude longitude)

(defun struct-to-plist (struct)
  (cl-loop for i upfrom 1
           for (slot . _) in (cdr (cl-struct-slot-info (type-of struct)))
           collect (intern (concat ":" (symbol-name slot)))
           collect (aref struct i)))

Then, say, convert to JSON:

(json-encode
 (struct-to-plist
  #s(poi "Grand Canyon" natural 36.3 -112.6)))
;; => {
;;      "name": "Grand Canyon",
;;      "category": "natural",
;;      "latitude": 36.3,
;;      "longitude": -112.6
;;    }

Re: Options for Structured Data in Emacs Lisp

Michael
Details
Message ID
<87wnv9b5q8.fsf@runbox.com>
In-Reply-To
<20210210230431.nbp5ejpj6dkosn7h@nullprogram.com> (view parent)
DKIM signature
pass
Download raw message
Yes, it turned out to be much simpler than I'd expected. Went
ahead and added support for reserved slots & structs expressed as
lists & vectors:

(defun struct-to-plist2 (x &rest params)
  "Serialize a CL struct X to a property list.

The type of X is assumed to be in the first slot; if that is not
so, pass the :type keyword parameter with either an integer
giving the slot in which the type resides, or a symbol naming the
type of X.

By default, the resulting plist will just contain the state
necessary to deserialize X; the reader will have to \"just know\"
the type of X on read. To tag the plist with the type, pass the
keyword argument :type-tag with a non-nil value.  In this case,
the plist will contain a property named :_type whose value will
be the structure name.

If the struct reserved slots using the :initial-offset value,
those slots will be serialized when non-nil with names of :_n
where n is the zero-baesd slot number."
  (let ((plist)
	(type-tag (plist-get params :type-tag))_)
    (cl-loop for i upfrom 0
	     for (slot . _) in
	     (cl-struct-slot-info
	      (let ((ty (plist-get params :type)))
		(cond
		 ((integerp ty) (aref x ty))
		 ((and ty (symbolp ty)) ty)
		 (t (aref x 0)))))
	     do
	     (let ((val (aref x i)))
	       (cond
		((eq slot 'cl-tag-slot)
		 (if type-tag
		     (setq plist (plist-put plist :_type val))))
		((eq slot 'cl-skip-slot)
		 (if val (setq plist (plist-put plist (intern 
		 (format ":_%d" i)) val))))
		(t
		 (if val (setq plist (plist-put plist (intern 
		 (concat ":" (symbol-name slot))) val)))))))
    plist))


Christopher Wellons <wellons@nullprogram.com> writes:

> You can reflect cl-defstruct to convert records to plists. For 
> instance:
>
> (cl-defstruct poi name category latitude longitude)
>
> (defun struct-to-plist (struct)
>  (cl-loop for i upfrom 1
>           for (slot . _) in (cdr (cl-struct-slot-info (type-of 
>           struct)))
>           collect (intern (concat ":" (symbol-name slot)))
>           collect (aref struct i)))
>
> Then, say, convert to JSON:
>
> (json-encode
> (struct-to-plist
>  #s(poi "Grand Canyon" natural 36.3 -112.6)))
> ;; => {
> ;;      "name": "Grand Canyon",
> ;;      "category": "natural",
> ;;      "latitude": 36.3,
> ;;      "longitude": -112.6
> ;;    }


-- 
Michael <sp1ff@runbox.com>
Reply to thread Export thread (mbox)