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