From nobody Mon Feb 15 17:28:48 2021 Authentication-Results: mail-b.sr.ht; dkim=pass header.d=runbox.com header.i=@runbox.com Received: from aibo.runbox.com (aibo.runbox.com [91.220.196.211]) by mail-b.sr.ht (Postfix) with ESMTPS id 3930211F004 for <~skeeto/public-inbox@lists.sr.ht>; Mon, 15 Feb 2021 17:28:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=selector2; h=Content-Type:MIME-Version:Message-ID:Date:In-reply-to:Subject :Cc:To:From:References; bh=CbgSgqYxx9CyrEOvUg8p8ScagEziE18jyK5uA1InD0k=; b=dd RJBeev8oT8dVN+NjhkIIKhPe/7DtiVHXywt4RyGahtogD6shMhwl1cRciL/ELFhUHkAPP4SClxtqB wDiCLx0udqAUwrmBIAS76VrTpTZ8yDK0odKOAEvFaKyvHMalqlYNOcuuPJxM6LhNg4qbSLn1fMEKa dv5oSvaIyIeoQ3ucr61do9QiT4tq4E+E3Cg4lcJIIdTe+FBD3oHtljg4DuT8WzMmQdWLL9ePID7zm KhQDYi08KPmkpdOpzjb6/5FnzrRSkAL8BMf6VUhpr9ziDnLeDG/N8i4AZQN3x7wmyXO5amBCZZmWQ a4I+2ACV8atCF7zIiUkeHDOMz80IUsrw==; Received: from [10.9.9.74] (helo=submission03.runbox) by mailtransmit03.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1lBhfw-0001Np-Ij; Mon, 15 Feb 2021 18:28:44 +0100 Received: by submission03.runbox with esmtpsa [Authenticated alias (942723)] (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) id 1lBhfm-0001VD-2w; Mon, 15 Feb 2021 18:28:34 +0100 References: <87zh0bc2gn.fsf@runbox.com> <20210210230431.nbp5ejpj6dkosn7h@nullprogram.com> User-agent: mu4e 1.2.0; emacs 28.0.50 From: Michael To: Christopher Wellons Cc: ~skeeto/public-inbox@lists.sr.ht Subject: Re: Options for Structured Data in Emacs Lisp In-reply-to: <20210210230431.nbp5ejpj6dkosn7h@nullprogram.com> Date: Mon, 15 Feb 2021 09:28:31 -0800 Message-ID: <87wnv9b5q8.fsf@runbox.com> MIME-Version: 1.0 Content-Type: text/plain; format=flowed 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 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