aconchillo / guile-json

JSON module for Guile
GNU General Public License v3.0
99 stars 35 forks source link

nullable fields in define-json-type macro #87

Open ebeem opened 1 month ago

ebeem commented 1 month ago

Thanks for the awesome work. I was wondering if it's possible to somehow allow nullable fields in the define-json-type macro. I think that this is indeed the expected behavior.

Example

(define-json-type <student>
  (id)
  (name)
  (major "major" <major>))

(define-json-type <major>
  (id)
  (name)
  (parent "parent" <major>))

(define json-str "{ \"id\": 1, \"name\": null, \"major\": null}")
(json->student json-str)

error

In procedure assoc: Wrong type argument in position 2 (expecting association list): null

changing the json-str to the below

builds a correct (not unspecified) scheme object

(define json-str "{ \"id\": 1, \"name\": null, \"major\": {}}")
(json->student json-str)

output

=> #<<student> id: 1 name: null major: #<<major> id: #<unspecified> name: #<unspecified> parent: #<unspecified>>>

also emitting the field major will produce correct results.

(define json-str "{ \"id\": 1, \"name\": null }")
(json->student json-str)

output

=> #<<student> id: 1 name: null major: #<unspecified>>

Anything wrong with my approach? Thanks again for your great work.

ebeem commented 1 month ago

I could solve it by modifying the extract-field macro to check for null values in addition to the pair check

      (let-syntax ((extract-field (syntax-rules ()
                                    ((_ table (field key scm->value value->scm))
                                     (scm->value (if (and (pair? (assoc key table)) (not (equal? 'null (cdr (assoc key table)))))
                                                     (cdr (assoc key table)) *unspecified*)))
                                    ((_ table (field key scm->value))
                                     (scm->value (if (pair? (assoc key table)) (cdr (assoc key table)) *unspecified*)))
                                    ((_ table (field key))
                                     (if (pair? (assoc key table)) (cdr (assoc key table)) *unspecified*))
                                    ((_ table (field))
                                     (if (pair? (assoc (symbol->string 'field) table)) (cdr (assoc (symbol->string 'field) table)) *unspecified*)))))
        (ctor (extract-field table spec) ...))
;; before
(scm->value (if (pair? (assoc key table)) (cdr (assoc key table)) *unspecified*))

;; after
(scm->value (if (and (pair? (assoc key table)) (not (equal? 'null (cdr (assoc key table)))))
                                                     (cdr (assoc key table)) *unspecified*))