guile-jsonld/jsonld/json.scm

json.scm

1
;;;; Copyright (C) 2019, 2020 Julien Lepiller <julien@lepiller.eu>
2
;;;; 
3
;;;; This library is free software; you can redistribute it and/or
4
;;;; modify it under the terms of the GNU Lesser General Public
5
;;;; License as published by the Free Software Foundation; either
6
;;;; version 3 of the License, or (at your option) any later version.
7
;;;; 
8
;;;; This library is distributed in the hope that it will be useful,
9
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
10
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11
;;;; Lesser General Public License for more details.
12
;;;; 
13
;;;; You should have received a copy of the GNU Lesser General Public
14
;;;; License along with this library; if not, write to the Free Software
15
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
;;;; 
17
18
(define-module (jsonld json)
19
  #:use-module (iri iri)
20
  #:use-module (json)
21
  #:use-module (jsonld download)
22
  #:use-module (web client)
23
  #:use-module (web response)
24
  #:use-module (web uri)
25
  #:use-module (rnrs bytevectors)
26
  #:use-module (ice-9 match)
27
  #:use-module (srfi srfi-1)
28
  #:use-module (srfi srfi-9)
29
  #:export (absolute-iri?
30
            alist-set
31
            alist-remove
32
            alist-sort-by-key
33
            array-add
34
            array-append
35
            blank-node?
36
            for-each-pair
37
            gen-delim?
38
            graph-object?
39
            json-array?
40
            json-has-key?
41
            json-keyword?
42
            json-null
43
            json-null?
44
            json-object?
45
            jsonld-error->string
46
            keyword-form?
47
            list-object?
48
            make-jsonld-options
49
            merge-json
50
            node-object?
51
            not-null-or-false
52
            processing-mode-1.0?
53
            processing-mode-1.1?
54
            relative-iri?
55
            same-json?
56
            identical-json?
57
            scalar?
58
            scalar-array?
59
            set-object?
60
            simple-graph-object?
61
            string-array?))
62
63
;; This module defines a bunch of functions used to test or modify json
64
;; documents.
65
66
(define-syntax for-each-pair
67
  (syntax-rules ()
68
    ((_ thunk alist)
69
     (for-each
70
       (match-lambda
71
         ((k . v) (thunk k v)))
72
       alist))))
73
74
(define (alist-set alist key value)
75
  "Return a new alist that is the same as @var{alist}, but whose @var{key}
76
is now associated with @var{value}.  This removes the old association of
77
@var{key} if any."
78
  (match alist
79
    (() (list (cons key value)))
80
    (((k . v) alist ...)
81
     (if (equal? k key)
82
       (cons (cons key value) alist)
83
       (cons (cons k v) (alist-set alist key value))))))
84
85
(define (alist-remove alist key)
86
  "Return a new alist that is the same as @var{alist}, but whose @var{key}
87
is not associated with anything anymore."
88
  (match alist
89
    (() '())
90
    (((k . v) alist ...)
91
     (if (equal? key k)
92
       alist
93
       (cons (cons k v) (alist-remove alist key))))))
94
95
(define (alist-key-less e1 e2)
96
  (match `(,e1 . ,e2)
97
    (((k1 . v1) . (k2 . v2))
98
     (string<=? k1 k2))))
99
100
(define* (alist-sort-by-key alist #:key (less alist-key-less))
101
  "Sort an alist @var{alist} by its keys."
102
  (sort alist less))
103
104
(define (scalar? v)
105
  "Whether a value is a scalar value, in the sense of the Json specification."
106
  (or (number? v) (string? v) (member v (list #t #f))))
107
108
(define (blank-node? node)
109
  "Whether a value is a blank node identifier, in the sense of the JsonLD
110
specification."
111
  (and (string? node) (> (string-length node) 1) (equal? (substring node 0 2) "_:")))
112
113
(define (json-keyword? k)
114
  "Whether a value @var{k} is a keyword, in the sense of the JsonLD specification,
115
version 1.1."
116
  (member
117
    k
118
    '(":" "@base" "@container" "@context" "@direction" "@graph" "@id" "@import"
119
      "@included" "@index" "@json" "@language" "@list" "@nest" "@none"
120
      "@prefix" "@propagate" "@protected" "@reverse" "@set" "@type" "@value"
121
      "@version" "@vocab")))
122
123
(define (json-array? v)
124
  "Whether a value is a Json array."
125
  (and (array? v) (not (string? v))))
126
127
(define (json-null? v)
128
  (equal? v 'null))
129
130
(define json-null 'null)
131
132
(define (json-object? v)
133
  "Whether a value is a Json object."
134
  (and (list? v) (not (json-null? v))))
135
136
(define (not-null-or-false v)
137
  (and (not (json-null? v)) v))
138
139
(define (json-has-key? obj key)
140
  "Whether a Json object @var{obj} has a @var{key}."
141
  (let loop ((obj obj))
142
    (match obj
143
      (((k . v) obj ...)
144
       (or (equal? k key) (loop obj)))
145
      (_ #f))))
146
147
(define (graph-object? v)
148
  (and (json-has-key? v "@graph")
149
       (null? (filter (lambda (kp) (not (member (car kp) '("@graph" "@id" "@index"))))
150
                      v))))
151
152
(define (simple-graph-object? v)
153
  (and (graph-object? v)
154
       (not (json-has-key? v "@id"))))
155
156
(define (list-object? v)
157
  (and (json-has-key? v "@list")
158
       (null? (filter (lambda (kp) (not (member (car kp) '("@list" "@index"))))
159
                      v))))
160
161
(define (set-object? v)
162
  (and (json-has-key? v "@list")
163
       (null? (filter (lambda (kp) (not (member (car kp) '("@list" "@index"))))
164
                      v))))
165
166
(define (absolute-iri? value)
167
  "Whether a value is an absolute IRI."
168
  (and (string? value)
169
       ;; XXX: actually, must be percent-encoded
170
       (not (string-any #\space value))
171
       ;; XXX: actually, this doesn't accept "ρ.ηλ" for instance
172
       (string->uri value)))
173
174
(define (relative-iri? value)
175
  "Whether a value is a relative IRI."
176
  (and (string? value) (string->uri-reference value)))
177
178
(define (string-array? v)
179
  (and (array? v) (null? (filter (lambda (v) (not (string? v))) (array->list v)))))
180
181
(define (scalar-array? v)
182
  (and (array? v) (null? (filter (lambda (v) (not (scalar? v))) (array->list v)))))
183
184
(define (array-add element array)
185
  (let ((array (or array #())))
186
    (list->array 1 (append (array->list array) (list element)))))
187
188
(define (array-append a1 a2)
189
  (let ((a1 (or a1 #()))
190
        (a2 (or a2 #())))
191
    (list->array 1 (append (array->list a1) (array->list a2)))))
192
193
(define (merge-json a b)
194
  (match b
195
    (() a)
196
    (((k . v) b ...)
197
     (if (json-has-key? a k)
198
         (merge-json a b)
199
         (merge-json (cons (cons k v) a) b)))))
200
201
(define (keyword-form? k)
202
  (and
203
    (string? k)
204
    (match (string->list k)
205
      (((? (lambda (k) (eq? k #\@)) l)
206
        (? (lambda (m) (char-set-contains? char-set:letter m)) m) ...)
207
       ;; only if there is actually something after @
208
       (> (string-length k) 1))
209
      (_ #f))))
210
211
(define (gen-delim? s)
212
  (string-every (char-set #\: #\/ #\? #\# #\[ #\] #\@) s))
213
214
(define (processing-mode-1.0? mode)
215
  (member mode '("jsonld-1.0" "json-ld-1.0")))
216
217
(define (processing-mode-1.1? mode)
218
  (member mode '("jsonld-1.1" "json-ld-1.1")))
219
220
(define (node-object? o)
221
  (and (json-object? o)
222
       (not (json-has-key? o "@value"))
223
       (not (json-has-key? o "@list"))
224
       (not (json-has-key? o "@set"))))
225
226
(define (has-identical-keys-of json other)
227
  (let loop ((json json) (result #t))
228
    (match json
229
      (#f (not other))
230
      (() result)
231
      (((key . value) json ...)
232
       (loop json (and result (identical-json? value (assoc-ref other key))))))))
233
234
(define (has-identical-values json other)
235
  (let loop ((json json) (other other) (result #t))
236
    (match json
237
      (() (if (null? other) result #f))
238
      ((v json ...)
239
       (match other
240
         (() #f)
241
         ((v2 other ...)
242
          (loop json other (and result (identical-json? v v2)))))))))
243
244
(define (identical-json? json other)
245
  "Compare two Json documents and returns whether they are the same, comparing
246
the keys, their values, their order and their presence in both documents.
247
This variant compares the value of blank nodes."
248
  (match json
249
    ((? array? json)
250
     (and
251
       (array? other)
252
       (has-identical-values (array->list json) (array->list other))))
253
    ((? list? json)
254
     (and (list? other) (has-identical-keys-of json other)
255
          (has-identical-keys-of other json)))
256
    (_ (equal? json other))))
257
258
(define (has-keys-of json other equivalences)
259
  (let loop ((json json) (equivalences equivalences))
260
    (match json
261
      (#f (and (not other) equivalences))
262
      (() equivalences)
263
      (((key . value) json ...)
264
       (if (blank-node? key)
265
           (if (assoc-ref equivalences key)
266
               (loop json (included-json?
267
                            value
268
                            (assoc-ref
269
                              other (assoc-ref equivalences key)) equivalences))
270
               (let loop2 ((candidates (filter (lambda (e) (blank-node? (car e)))
271
                                               other)))
272
                 (match candidates
273
                   (() #f)
274
                   (((k . v) candidates ...)
275
                    (let ((res (included-json?
276
                                 value
277
                                 v
278
                                 (cons (cons key k) equivalences))))
279
                      (if res res (loop2 candidates)))))))
280
           (loop json (included-json? value (assoc-ref other key) equivalences)))))))
281
282
(define (has-same-values json other equivalences)
283
  (let loop ((json json) (other other) (equivalences equivalences))
284
    (match json
285
      (() (if (null? other) equivalences #f))
286
      ((v json ...)
287
       (match other
288
         (() #f)
289
         ((v2 other ...)
290
          (loop json other (included-json? v v2 equivalences))))))))
291
292
(define (included-json? json other equivalences)
293
  (match json
294
    ((? json-array? json)
295
     (and
296
       (array? other)
297
       (has-same-values (array->list json) (array->list other) equivalences)))
298
    ((? list? json)
299
     (and (list? other) (has-keys-of json other equivalences)))
300
    ((? blank-node? json)
301
     (and (blank-node? other)
302
          (if (assoc-ref json equivalences)
303
              (and (equal? (assoc-ref json equivalences) other) equivalences)
304
              (cons (cons json other) equivalences))))
305
    (_ (and (equal? json other) equivalences))))
306
307
(define (same-json? json other)
308
  "Compare two Json documents and returns whether they are the same, comparing
309
the keys, their values, their order and their presence in both documents.
310
This variant tries to map blank nodes from one object to the other."
311
  (and (included-json? json other '()) (included-json? other json '())))
312
313
(define (jsonld-error->string err)
314
  "Convert an error to a string."
315
  (match err
316
    ('colliding-keywords "colliding keywords")
317
    ('conflicting-indexes "conflicting indexes")
318
    ('cyclic-iri-mapping "cyclic IRI mapping")
319
    ('invalid-@id-value "invalid @id value")
320
    ('invalid-@index-value "invalid @index value")
321
    ('invalid-@nest-value "invalid @nest value")
322
    ('invalid-@prefix-value "invalid @prefix value")
323
    ('invalid-@propagate-value "invalid @propagate value")
324
    ('invalid-@protected-value "invalid @protected value")
325
    ('invalid-@reverse-value "invalid @reverse value")
326
    ('invalid-@import-value "invalid @import value")
327
    ('invalid-@version-value "invalid @version value")
328
    ('invalid-base-iri "invalid base IRI")
329
    ('invalid-base-direction "invalid base direction")
330
    ('invalid-container-mapping "invalid container mapping")
331
    ('invalid-context-entry "invalid context entry")
332
    ('invalid-context-nullification "invalid context nullification")
333
    ('invalid-default-language "invalid default language")
334
    ('invalid-@included-value "invalid @included value")
335
    ('invalid-iri-mapping "invalid IRI mapping")
336
    ('invalid-json-literal "invalid JSON literal")
337
    ('invalid-keyword-alias "invalid keyword alias")
338
    ('invalid-language-map-value "invalid language map value")
339
    ('invalid-language-mapping "invalid language mapping")
340
    ('invalid-language-tagged-string "invalid language-tagged string")
341
    ('invalid-language-tagged-value "invalid language-tagged value")
342
    ('invalid-local-context "invalid local context")
343
    ('invalid-remote-context "invalid remote context")
344
    ('invalid-reverse-property "invalid reverse property")
345
    ('invalid-reverse-property-map "invalid reverse property map")
346
    ('invalid-reverse-property-value "invalid reverse property value")
347
    ('invalid-scoped-context "invalid scoped context")
348
    ('invalid-script-element "invalid script element")
349
    ('invalid-set-or-list-object "invalid set or list object")
350
    ('invalid-term-definition "invalid term definition")
351
    ('invalid-type-mapping "invalid type mapping")
352
    ('invalid-type-value "invalid type value")
353
    ('invalid-typed-value "invalid typed value")
354
    ('invalid-value-object "invalid value object")
355
    ('invalid-value-object-value "invalid value object value")
356
    ('invalid-vocab-mapping "invalid vocab mapping")
357
    ('iri-confused-with-prefix "IRI confused with prefix")
358
    ('keyword-redefinition "keyword redefinition")
359
    ('loading-document-failed "loading document failed")
360
    ('loading-remote-context-failed "loading remote context failed")
361
    ('multiple-context-link-headers "multiple context link headers")
362
    ('processing-mode-conflict "processing mode conflict")
363
    ('protected-term-redefinition "protected term redefinition")
364
    ('context-overflow "context overflow")
365
    (_ (format #f "unknown error ~a" err))))
366