aconchillo / guile-json

JSON module for Guile
GNU General Public License v3.0
101 stars 34 forks source link

Add scm->value and value->scm specifications to define-json-type #82

Open vintnes opened 1 year ago

vintnes commented 1 year ago

Hi Aleix. Thank you for this solid library I use daily.

It's kind of painful when a JSON type with a nested record type needs to implement some value conversion.

Example

(use-modules (json))

(define simplified-real-world-json
"{\"initializationDate\":\"2018-06-19T08:13:04.29+02:00\",\"product\":\"ICE\",\"trainNumber\":\"369\",\"serviceId\":\"1529131014\",\"wagons\":[{\"group\":0,\"type\":\"TRIEBKOPF\",\"id\":\"938054015830\",\"wagonNumber\":null,\"status\":\"OFFEN\"}]}")

(define-json-type <manifest>
  (initialization-date "initializationDate")
  (product)
  (train-number "trainNumber")
  (service-id "serviceId")
  (wagons "wagons" #(<wagon>)))

(define-json-type <wagon>
  (group) (type) (id) (number "wagonNumber") (status))

Now let's say we'd like to operate on the train number (simpler than the date for illustration purposes), delivered to us by the grace of German engineering in string format. Reformatting according to define-json-mapping is a little tedious but overall worth the convenience/granularity tradeoff in my opinion. The painful part is that we also have to manually map any nested records, which isn't related to the problem we're working on.

(use-modules (ice-9 match))

(define wagon-converter
  (match-lambda (#((= scm->wagon wagons) ...) wagons)
                (((= wagon->scm wagons) ...) (list->vector wagons))))

(define-json-mapping <manifest> manifest manifest?
  json->manifest <=> manifest->json
  (initialization-date manifest-initialization-date "initializationDate")
  (product manifest-product)
  (train-number manifest-train-number "trainNumber" string->number number->string)
  (service-id manifest-service-id "serviceId")
  (wagons manifest-wagons "wagons" wagon-converter wagon-converter))

(call-with-input-string simplified-real-world-json json->manifest)

Am I just missing something simple? Is there any reason why define-json-type can't support the scm->value and value->scm field specifications? Have you considered using keywords over positional parameters, or would you be open to that change in the future? Thanks again for your support.

aconchillo commented 1 year ago

hi @vintnes ! Yes, it's definitely a little bit more painful. I added define-json-type to be able to define simple JSON objects in a very simple way, but when it comes to flexibility you run into the issue you are having now. It might be worth adding keyword arguments there as you suggest, for example:

(define-json-type <manifest>
  (initialization-date "initializationDate")
  (product)
  (train-number "trainNumber" #:encode number->string #:decode string->number)
  (service-id "serviceId")
  (wagons "wagons" #(<wagon>)))
aconchillo commented 1 year ago

I don't have much time lately but I think it would be a nice addition. I'll see if I find some time soon.