;;;; Copyright (C) 2019, 2020 Julien Lepiller ;;;; ;;;; This library is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU Lesser General Public ;;;; License as published by the Free Software Foundation; either ;;;; version 3 of the License, or (at your option) any later version. ;;;; ;;;; This library is distributed in the hope that it will be useful, ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;;; Lesser General Public License for more details. ;;;; ;;;; You should have received a copy of the GNU Lesser General Public ;;;; License along with this library; if not, write to the Free Software ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ;;;; (define-module (jsonld) #:use-module (iri iri) #:use-module (jsonld compaction) #:use-module (jsonld context) #:use-module (jsonld context-processing) #:use-module (jsonld deserialize-jsonld) #:use-module (jsonld download) #:use-module (jsonld expansion) #:use-module (jsonld flattening) #:use-module (jsonld generate-blank-node-identifier) #:use-module (jsonld inverse-context-creation) #:use-module (jsonld iri-compaction) #:use-module (jsonld json) #:use-module (jsonld node-map-generation) #:use-module (jsonld options) #:use-module (jsonld serialize-rdf) #:use-module (rdf rdf) #:export (compact expand flatten jsonld->rdf rdf->jsonld)) (define* (compact input context #:key (options (new-jsonld-options))) (call-with-values (lambda () ;; TODO: set ordered to #f (expand-with-base input #:options options)) ;; 2 and 3 (lambda (expanded-input context-base) (when (string? context) (set! context (json-document-document ((jsonld-options-document-loader options) context)))) ;; 4 (when (json-has-key? context "@context") (set! context (assoc-ref context "@context"))) (let* ((base-iri (or (jsonld-options-base options) (and (jsonld-options-compact-to-relative? options) (string? input) input))) (active-context (context-processing (new-active-context #:base base-iri) context context-base #:options options)) (inverse-context (inverse-context-creation active-context)) (compacted-output (compaction active-context inverse-context json-null ;; active-property expanded-input ;; element #:compact-arrays? (jsonld-options-compact-arrays? options) #:ordered? (jsonld-options-ordered? options) #:processing-mode (jsonld-options-processing-mode options)))) (if (equal? compacted-output #()) (set! compacted-output '()) (when (json-array? compacted-output) (set! compacted-output `((,(iri-compaction active-context inverse-context "@graph" #:vocab? #t #:reverse? #f) . ,compacted-output))))) (when (and context (not (null? compacted-output)) (not (null? context))) (set! compacted-output (cons (cons "@context" context) compacted-output))) compacted-output)))) (define* (expand-with-base input #:key (options (new-jsonld-options))) (let ((document input) (remote-document #f) (active-context (new-active-context #:base (jsonld-options-base options) #:original-base (jsonld-options-base options))) (document-base (jsonld-options-base options))) (when (string? input) ;; 2 (catch #t (lambda () (set! remote-document ((jsonld-options-document-loader options) input #:extract-all-scripts? (jsonld-options-extract-all-scripts? options)))) (lambda (key . value) (cond ((member key '(loading-document-failed multiple-context-link-headers invalid-script-element)) (apply throw key value)) (else (apply throw 'loading-document-failed key value))))) ;; 3 (set! document (json-document-document remote-document)) (set! document-base (json-document-document-url remote-document)) ;; 4 (set! active-context (update-active-context active-context #:base (or (jsonld-options-base options) (json-document-document-url remote-document)) #:original-base (or (json-document-document-url remote-document) (jsonld-options-base options))))) ;; 5 (when (jsonld-options-expand-context options) (set! active-context (let* ((local-context (jsonld-options-expand-context options)) (local-context (if (json-has-key? local-context "@context") (assoc-ref local-context "@context") local-context))) (context-processing active-context local-context (active-context-original-base active-context))))) ;; 6 (when (and remote-document (json-document-context-url remote-document)) (let ((context-url (json-document-context-url remote-document))) (set! active-context (context-processing active-context context-url context-url)))) ;; 7 (let ((expanded-output (expansion active-context json-null document (if remote-document (or (json-document-document-url remote-document) (jsonld-options-base options)) (jsonld-options-base options)) #:options options))) (when (and (json-object? expanded-output) (json-has-key? expanded-output "@graph") (null? (filter (lambda (kv) (not (equal? (car kv) "@graph"))) expanded-output))) (set! expanded-output (assoc-ref expanded-output "@graph"))) (when (json-null? expanded-output) (set! expanded-output #())) (unless (json-array? expanded-output) (set! expanded-output `#(,expanded-output))) ;; 8 (values expanded-output document-base)))) (define* (expand input #:key (options (new-jsonld-options))) (call-with-values (lambda () (expand-with-base input #:options options)) (lambda (out base) out))) (define* (flatten input #:key (context #f) (options (new-jsonld-options))) ;; 2 (let* ((expanded-input (expand input #:options (update-jsonld-options options #:ordered? #t))) ;; 3 (base-iri (or (jsonld-options-base options) (and (jsonld-options-compact-to-relative? options) (string? input) input))) ;; 4 (identifier-map '()) ;; 5 (flattened-output (flattening expanded-input #:ordered? (jsonld-options-ordered? options)))) ;; 5.1 (when context ;; TODO (set! flattened-output (compact flattened-output context #:options options))) flattened-output)) (define* (jsonld->rdf input #:key (options (new-jsonld-options))) (call-with-values (lambda () ;; TODO: set ordered to #f (expand-with-base input #:options options)) ;; 2 and 3 (lambda (expanded-input context-base) (let* ((generate-blank-node (get-generate-blank-node-identifier)) (generate-node-map (get-node-map-generation generate-blank-node)) (node-map (generate-node-map expanded-input '())) (node-map (assoc-ref node-map "node-map"))) (deserialize-jsonld generate-blank-node node-map (make-rdf-dataset '() '()) #:produce-generalized-rdf? (jsonld-options-produce-generalized-rdf? options) #:rdf-direction (jsonld-options-rdf-direction options)))))) (define* (rdf->jsonld input #:key (options (new-jsonld-options))) (serialize-rdf input #:ordered? (jsonld-options-ordered? options) #:rdf-direction (jsonld-options-rdf-direction options) #:use-native-types? (jsonld-options-use-native-types? options) #:use-rdf-type? (jsonld-options-use-rdf-type? options) #:processing-mode (jsonld-options-processing-mode options)))