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