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 (jsonld deserialize-jsonld) |
| 21 | #:use-module (jsonld iri) |
| 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 |