serialize-rdf.scm
1 | ;;;; Copyright (C) 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 serialize-rdf) |
19 | #:use-module (ice-9 match) |
20 | #:use-module (iri iri) |
21 | #:use-module (jsonld deserialize-jsonld) |
22 | #:use-module (jsonld json) |
23 | #:use-module (jsonld object-to-rdf) |
24 | #:use-module (jsonld rdf-to-object) |
25 | #:use-module (json) |
26 | #:use-module ((rdf rdf) #:hide (blank-node?)) |
27 | #:use-module ((rdf rdf) #:select (blank-node?) #:prefix rdf:) |
28 | #:use-module (srfi srfi-1) |
29 | #:export (serialize-rdf)) |
30 | |
31 | (define (uniq lst) |
32 | (match lst |
33 | (() '()) |
34 | ((element lst ...) |
35 | (if (member element lst) |
36 | (uniq lst) |
37 | (cons element (uniq lst)))))) |
38 | |
39 | (define (jsonld-ref object spec) |
40 | (match spec |
41 | (() object) |
42 | (((? string? key) spec ...) |
43 | (jsonld-ref (assoc-ref object key) spec)) |
44 | (((? number? key) spec ...) |
45 | (jsonld-ref (if (array? object) (array-ref object key) #f) spec)))) |
46 | |
47 | (define (jsonld-set object spec value) |
48 | (match spec |
49 | (() value) |
50 | (((? string? key) spec ...) |
51 | (alist-set object key (jsonld-set (assoc-ref object key) spec value))) |
52 | (((? number? key) spec ...) |
53 | (array-set! object (jsonld-set (array-ref object key) spec value) key) |
54 | object))) |
55 | |
56 | (define (convert-blank-nodes dataset) |
57 | (define (node-convert-blank-nodes node) |
58 | (if (rdf:blank-node? node) |
59 | (string-append "_:b" (number->string node)) |
60 | node)) |
61 | |
62 | (define (graph-convert-blank-nodes graph) |
63 | (map |
64 | (match-lambda |
65 | (($ rdf-triple subject predicate object) |
66 | (make-rdf-triple |
67 | (node-convert-blank-nodes subject) |
68 | (node-convert-blank-nodes predicate) |
69 | (node-convert-blank-nodes object)))) |
70 | graph)) |
71 | |
72 | (make-rdf-dataset |
73 | (graph-convert-blank-nodes (rdf-dataset-default-graph dataset)) |
74 | (map |
75 | (lambda (ng) |
76 | (cons (car ng) |
77 | (graph-convert-blank-nodes (cdr ng)))) |
78 | (rdf-dataset-named-graphs dataset)))) |
79 | |
80 | (define (single-element-array? object) |
81 | (and (array? object) (not (string? object)) |
82 | (equal? (array-length object) 1))) |
83 | |
84 | (define* (serialize-rdf dataset |
85 | #:key |
86 | ordered? rdf-direction use-native-types? use-rdf-type? |
87 | processing-mode) |
88 | (set! dataset (convert-blank-nodes dataset)) |
89 | (set! dataset |
90 | (make-rdf-dataset |
91 | (uniq (rdf-dataset-default-graph dataset)) |
92 | (map |
93 | (lambda (ng) |
94 | (cons (car ng) |
95 | (uniq (cdr ng)))) |
96 | (rdf-dataset-named-graphs dataset)))) |
97 | ;; 1 |
98 | (let ((default-graph '()) |
99 | ;; 2 |
100 | (graph-map `(("@default" . ()))) |
101 | ;; 3 |
102 | (referenced-once '()) |
103 | ;; 4 |
104 | (compound-literal-subjects '())) |
105 | ;; 5 |
106 | (for-each-pair |
107 | (lambda (name graph) |
108 | ;; 5.2 |
109 | (unless (json-has-key? graph-map name) |
110 | (set! graph-map (alist-set graph-map name '()))) |
111 | ;; 5.3 |
112 | (unless (json-has-key? compound-literal-subjects name) |
113 | (set! compound-literal-subjects |
114 | (alist-set compound-literal-subjects name '()))) |
115 | (set! default-graph (assoc-ref graph-map "@default")) |
116 | ;; 5.4 |
117 | (unless (or (equal? name "@default") |
118 | (json-has-key? default-graph name)) |
119 | (set! default-graph (alist-set (assoc-ref graph-map "@default") |
120 | name `(("@id" . ,name)))) |
121 | (set! graph-map (alist-set graph-map "@default" default-graph))) |
122 | ;; 5.5 |
123 | (let ((node-map (assoc-ref graph-map name)) |
124 | ;; 5.6 |
125 | (compound-map (assoc-ref compound-literal-subjects name))) |
126 | (for-each |
127 | (match-lambda |
128 | (($ rdf-triple subject predicate object) |
129 | ;; 5.7.1 |
130 | (unless (json-has-key? node-map subject) |
131 | (set! node-map (alist-set node-map subject `(("@id" . ,subject))))) |
132 | ;; 5.7.2 |
133 | (let ((node-ref (list name subject)) |
134 | (node (assoc-ref node-map subject)) |
135 | (continue? #t)) |
136 | ;; 5.7.3 |
137 | (when (and (equal? rdf-direction "compound-literal") |
138 | (equal? predicate (rdf-iri "direction"))) |
139 | (set! compound-map (alist-set compound-map subject #t))) |
140 | (when (or (absolute-iri? object) (blank-node? object)) |
141 | ;; 5.7.4 |
142 | (unless (json-has-key? node-map object) |
143 | (set! node-map (alist-set node-map object `(("@id" . ,object))))) |
144 | ;; 5.7.5 |
145 | (when (and (equal? predicate (rdf-iri "type")) |
146 | (not use-rdf-type?)) |
147 | (let* ((types (or (assoc-ref node "@type") #())) |
148 | (types (array->list types)) |
149 | (types (if (member object types) |
150 | types |
151 | (append types (list object)))) |
152 | (types (list->array 1 types))) |
153 | (set! node (alist-set node "@type" types)) |
154 | (set! node-map (alist-set node-map subject node)) |
155 | (set! continue? #f)))) |
156 | ;; 5.7.6 |
157 | (when continue? |
158 | (let* ((value (rdf-to-object object rdf-direction use-native-types? |
159 | #:processing-mode |
160 | processing-mode)) |
161 | (value-ref (append node-ref (list predicate))) |
162 | ;; 5.7.7 |
163 | (node-value (or (assoc-ref node predicate) #())) |
164 | (node-value (array->list node-value))) |
165 | ;; 5.7.8 |
166 | (let loop ((nodes node-value) (num 0)) |
167 | (match nodes |
168 | (() |
169 | (set! node-value (cons value node-value)) |
170 | (set! value-ref (append value-ref (list num)))) |
171 | ((n nodes ...) |
172 | (if (identical-json? value n) |
173 | (set! value-ref (append value-ref (list num))) |
174 | (loop nodes (1+ num)))))) |
175 | ;; 5.7.7 |
176 | (set! node (alist-set node predicate (list->array 1 node-value))) |
177 | (set! node-map (alist-set node-map subject node)) |
178 | (cond |
179 | ;; 5.7.9 |
180 | ((equal? object (rdf-iri "nil")) |
181 | (let* ((usages (or (assoc-ref (assoc-ref node-map object) |
182 | "usages") |
183 | #())) |
184 | (usages (array->list usages))) |
185 | (set! usages |
186 | (append |
187 | usages |
188 | (list `(("node" . ,node-ref) |
189 | ("property" . ,predicate) |
190 | ("value" . ,value-ref))))) |
191 | (set! node-map |
192 | (jsonld-set |
193 | node-map (list object "usages") |
194 | (list->array 1 usages))))) |
195 | ;; 5.7.10 |
196 | ((json-has-key? referenced-once object) |
197 | (set! referenced-once |
198 | (alist-set referenced-once object #f))) |
199 | ((blank-node? object) |
200 | (set! referenced-once |
201 | (alist-set referenced-once object |
202 | `(("node" . ,node-ref) |
203 | ("property" . ,predicate) |
204 | ("value" . ,value-ref))))))))))) |
205 | graph) |
206 | (set! graph-map (alist-set graph-map name node-map)) |
207 | (set! compound-literal-subjects |
208 | (alist-set compound-literal-subjects name compound-map)))) |
209 | (cons (cons "@default" (rdf-dataset-default-graph dataset)) |
210 | (rdf-dataset-named-graphs dataset))) |
211 | (set! default-graph (assoc-ref graph-map "@default")) |
212 | ;; 6 |
213 | (for-each-pair |
214 | (lambda (name graph-object) |
215 | ;; 6.1 |
216 | (when (list? (assoc-ref compound-literal-subjects name)) |
217 | (for-each-pair |
218 | (lambda (cl _) |
219 | ;; 6.1.1 |
220 | (let ((cl-entry (assoc-ref referenced-once cl))) |
221 | (when (list? cl-entry) |
222 | ;; 6.1.2 |
223 | (let* ((node-ref (assoc-ref cl-entry "node")) |
224 | (node (jsonld-ref graph-map node-ref)) |
225 | ;; 6.1.3 |
226 | (property (assoc-ref cl-entry "property")) |
227 | ;; 6.1.4 |
228 | (value-ref (assoc-ref cl-entry "value")) |
229 | (value (jsonld-ref graph-map value-ref)) |
230 | ;; 6.1.5 |
231 | (cl-node (assoc-ref graph-object cl))) |
232 | (set! graph-object (alist-remove graph-object cl)) |
233 | (when (equal? name "@default") |
234 | (set! default-graph graph-object)) |
235 | (set! graph-map (alist-set graph-map name graph-object)) |
236 | (when (list? cl-node) |
237 | ;; 6.1.6 |
238 | (let* ((refs (array->list (assoc-ref node property)))) |
239 | (set! refs |
240 | (map |
241 | (lambda (cl-reference) |
242 | (when (equal? (assoc-ref cl-reference "@id") cl) |
243 | ;; 6.1.6.1 |
244 | (set! cl-reference (alist-remove cl-reference "@id")) |
245 | ;; 6.1.6.2 |
246 | (let* ((value (assoc-ref cl-node (rdf-iri "value"))) |
247 | (value (array-ref value 0)) |
248 | (value (assoc-ref value "@value"))) |
249 | (set! cl-reference |
250 | (alist-set cl-reference "@value" value))) |
251 | ;; 6.1.6.3 |
252 | (let ((language (assoc-ref cl-node (rdf-iri "language")))) |
253 | (when language |
254 | (let* ((language (array-ref language 0)) |
255 | (language (assoc-ref language "@value"))) |
256 | (set! cl-reference |
257 | (alist-set cl-reference "@language" language)) |
258 | (unless (well-formed-language-tag? language) |
259 | (throw 'invalid-language-tagged-string language))))) |
260 | ;; 6.1.6.4 |
261 | (let ((direction (assoc-ref cl-node (rdf-iri "direction")))) |
262 | (when direction |
263 | (let* ((direction (array-ref direction 0)) |
264 | (direction (assoc-ref direction "@value"))) |
265 | (set! cl-reference |
266 | (alist-set cl-reference "@direction" direction)) |
267 | (unless (member direction '("ltr" "rtl")) |
268 | (throw 'invalid-base-direction direction)))))) |
269 | cl-reference) |
270 | refs)) |
271 | (set! node (alist-set node property (list->array 1 refs)))) |
272 | (set! graph-map (jsonld-set graph-map node-ref node))))))) |
273 | (assoc-ref compound-literal-subjects name))) |
274 | ;; 6.2 |
275 | (when (json-has-key? graph-object (rdf-iri "nil")) |
276 | ;; 6.3 |
277 | (let* ((nil (assoc-ref graph-object (rdf-iri "nil"))) |
278 | (usages (array->list (or (assoc-ref nil "usages") #())))) |
279 | (set! usages |
280 | (sort usages (lambda (a b) |
281 | (or (not (equal? (assoc-ref a "node") (assoc-ref b "node"))) |
282 | (and (equal? (assoc-ref a "property") (rdf-iri "first")) |
283 | (equal? (assoc-ref b "property") (rdf-iri "rest"))))))) |
284 | ;; 6.4 |
285 | (for-each |
286 | (lambda (usage) |
287 | (let* ((node-ref (assoc-ref usage "node")) |
288 | (node (jsonld-ref graph-map node-ref)) |
289 | (property (assoc-ref usage "property")) |
290 | (head-ref (assoc-ref usage "value")) |
291 | (head (jsonld-ref graph-map head-ref)) |
292 | ;; 6.4.2 |
293 | (lst '()) |
294 | (list-nodes '())) |
295 | ;; 6.4.3 |
296 | (let loop () |
297 | (when (and (equal? property (rdf-iri "rest")) |
298 | (blank-node? (assoc-ref node "@id")) |
299 | (list? (assoc-ref referenced-once |
300 | (assoc-ref node "@id"))) |
301 | (single-element-array? (assoc-ref node (rdf-iri "first"))) |
302 | (single-element-array? (assoc-ref node (rdf-iri "rest"))) |
303 | (null? (filter |
304 | (lambda (e) |
305 | (not (member (car e) |
306 | (list (rdf-iri "first") |
307 | (rdf-iri "rest") |
308 | "@type" |
309 | "@id")))) |
310 | node)) |
311 | (or (not (json-has-key? node "@type")) |
312 | (null? |
313 | (filter |
314 | (lambda (t) |
315 | (not (equal? t (rdf-iri "List")))) |
316 | (array->list (assoc-ref node "@type")))))) |
317 | ;; 6.4.3.1 |
318 | (set! lst |
319 | (cons (array-ref (assoc-ref node (rdf-iri "first")) 0) |
320 | lst)) |
321 | ;; 6.4.3.2 |
322 | (set! list-nodes |
323 | (cons (assoc-ref node "@id") list-nodes)) |
324 | ;; 6.4.3.3 |
325 | (let ((node-usage (assoc-ref referenced-once |
326 | (assoc-ref node "@id")))) |
327 | ;; 6.4.3.4 |
328 | (set! node-ref (assoc-ref node-usage "node")) |
329 | (set! node (jsonld-ref graph-map node-ref)) |
330 | (set! property (assoc-ref node-usage "property")) |
331 | (set! head-ref (assoc-ref node-usage "value")) |
332 | (set! head (jsonld-ref graph-map head-ref)) |
333 | (loop)))) |
334 | ;; 6.4.4 |
335 | (set! head (alist-remove head "@id")) |
336 | ;; 6.4.6 |
337 | (set! head (alist-set head "@list" (list->array 1 lst))) |
338 | (set! graph-map (jsonld-set graph-map head-ref head)) |
339 | ;; 6.4.7 |
340 | (for-each |
341 | (lambda (node-id) |
342 | (set! graph-object (alist-remove graph-object node-id)) |
343 | (when (equal? name "@default") |
344 | (set! default-graph graph-object)) |
345 | (set! graph-map (alist-set graph-map name graph-object))) |
346 | list-nodes))) |
347 | usages)))) |
348 | graph-map) |
349 | (set! default-graph (assoc-ref graph-map "@default")) |
350 | ;; 7 |
351 | (let ((result '())) |
352 | ;; 8 |
353 | (for-each-pair |
354 | (lambda (subject node) |
355 | ;; 8.1 |
356 | (when (json-has-key? graph-map subject) |
357 | (let ((new-graph '())) |
358 | (for-each-pair |
359 | (lambda (s n) |
360 | ;; 8.1.2 |
361 | (unless (null? (filter |
362 | (lambda (e) |
363 | (not (member (car e) '("usages" "@id")))) |
364 | n)) |
365 | (set! new-graph |
366 | (append new-graph (list (alist-remove n "usages")))))) |
367 | (if ordered? |
368 | (alist-sort-by-key (or (assoc-ref graph-map subject) '())) |
369 | (or (assoc-ref graph-map subject) '()))) |
370 | ;; 8.1.1 |
371 | (set! node (alist-set node "@graph" (list->array 1 new-graph))))) |
372 | ;; 8.2 |
373 | (unless (null? (filter |
374 | (lambda (e) |
375 | (not (member (car e) '("usages" "@id")))) |
376 | node)) |
377 | (set! result |
378 | (append result (list (alist-remove node "usages")))))) |
379 | (if ordered? |
380 | (alist-sort-by-key default-graph) |
381 | default-graph)) |
382 | ;; 9 |
383 | (list->array 1 result)))) |
384 |