Skip to content

define-json-mapping allows nonsensical key subforms #79

Description

@LiberalArtist

I recently wrote something like the following buggy code:

(define-json-mapping <project-info> make-project-info project-info?
  json->project-info
  (license project-info-license
           (lambda (x)
             (if (string? x)
                 (spdx-string->license x)
                 #f))))

and was confused to find that project-info-license always produced *unspecified*.

Eventually, I worked out that I needed instead to write:

(define-json-mapping <project-info> make-project-info project-info?
  json->project-info
  (license project-info-license
           "license" (lambda (x)
                       (if (string? x)
                           (spdx-string->license x)
                           #f))))

i.e. that writing "license" was required even though it matched license.

Originally, I had read the grammar at:

guile-json/README.md

Lines 331 to 348 in 81bc5da

- *((field getter ...) ...)* : a series of field specifications.
- *field* : the name of a JSON object field.
- *getter* : the name of the procedure to get the value of this field
given a record of this type.
- *key* : a different name for the field of this JSON object. If given, this
name will be used instead of the field name when serializing or
deserializing.
- *scm->value* : an optional procedure that will be used to convert native
values supported by guile-json to the value contained in the record. Used
when reading JSON.
- *value->scm* : an optional procedure that will be used to convert the
value contained in the record to a native value supported by guile-json
Used when writing JSON.

to mean the field and getter subforms were required, but the key, scm->value, and value->scm subforms were optional, so I thought I would only need to specify key if it were different than field. However, it seems like what I intended as a scm->value expression was being treated as a key, and of course there would never be a key equal? to that procedure, so I ended up with *unspecified* and no scm->value procedure to convert it.

I can see a few possible improvements, depending on what behavior is preferred:

  1. If you want key to be optional in the way I originally imagined:

    1. The expansion of define-json-mapping could check at run-time if the first of the optional sub-forms evaluates to a string and treat it as key if so; scm->value otherwise.

    2. If key is restricted to a string literal, rather than an expression that evaluates to a string, define-json-mapping could perform such a check at compile-time.

  2. If you want key to be required in order to provide scm->value, I think define-json-mapping should still check, either at run-time or compile-time, that key actually is/evaluates to a string, since supplying a procedure for key would never be a reasonable thing to do.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions