object-to-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 object-to-rdf) |
19 | #:use-module (ice-9 match) |
20 | #:use-module (ice-9 regex) |
21 | #:use-module (jsonld deserialize-jsonld) |
22 | #:use-module (jsonld iri) |
23 | #:use-module (jsonld json) |
24 | #:use-module (jsonld list-to-rdf) |
25 | #:use-module (json) |
26 | #:use-module ((rdf rdf) #:hide (blank-node?)) |
27 | #:use-module (srfi srfi-1) |
28 | #:export (object-to-rdf |
29 | well-formed-language-tag?)) |
30 | |
31 | (define (canonical-double value) |
32 | (let ((exponent (inexact->exact (floor (log10 value))))) |
33 | (string-append |
34 | (number->string (exact->inexact (/ value (expt 10 exponent)))) |
35 | "E" |
36 | (number->string exponent)))) |
37 | |
38 | (define (canonical-json value) |
39 | (cond |
40 | ((member value `(#t #f ,json-null)) |
41 | value) |
42 | ((string? value) value) |
43 | ((number? value) |
44 | (if (integer? (inexact->exact value)) |
45 | (inexact->exact value) |
46 | value)) |
47 | ((list? value) |
48 | (map |
49 | (match-lambda |
50 | ((k . v) |
51 | (cons k (canonical-json v)))) |
52 | (alist-sort-by-key value))) |
53 | ((array? value) |
54 | (list->array |
55 | 1 |
56 | (fold-right |
57 | (lambda (val result) |
58 | (cons (canonical-json val) result)) |
59 | '() |
60 | (array->list value)))))) |
61 | |
62 | (define (well-formed-language-tag? tag) |
63 | (let ((match (string-match "[a-z]{1,8}(-[0-9a-z]{1,8})*" tag))) |
64 | (if match |
65 | (let* ((match (cadr (array->list match))) |
66 | (fst (car match)) |
67 | (snd (cdr match))) |
68 | (and (equal? fst 0) |
69 | (equal? snd (string-length tag)))) |
70 | #f))) |
71 | |
72 | (define* (object-to-rdf generate-blank-node rdf-direction item list-triples) |
73 | (let ((result json-null)) |
74 | (cond |
75 | ;; 1 |
76 | ((and (node-object? item) (not (well-formed? (assoc-ref item "@id")))) |
77 | (set! result json-null)) |
78 | ;; 2 |
79 | ((node-object? item) |
80 | (set! result (assoc-ref item "@id"))) |
81 | ;; 3 |
82 | ((list-object? item) |
83 | (let* ((res (list-to-rdf generate-blank-node rdf-direction |
84 | (array->list (assoc-ref item "@list")) |
85 | list-triples)) |
86 | (lst (assoc-ref res "list-triples")) |
87 | (res (assoc-ref res "result"))) |
88 | (set! result res) |
89 | (set! list-triples lst))) |
90 | (else |
91 | ;; 4 |
92 | (let ((value (assoc-ref item "@value")) |
93 | (datatype (or (assoc-ref item "@type") json-null))) |
94 | (cond |
95 | ;; 6 |
96 | ((and (not (json-null? datatype)) (not (equal? datatype "@json")) |
97 | (not (well-formed? datatype))) |
98 | (set! result json-null)) |
99 | ;; 7 |
100 | ((and |
101 | (json-has-key? item "@language") |
102 | (not (well-formed-language-tag? (assoc-ref item "@language")))) |
103 | (set! result json-null)) |
104 | (else |
105 | ;; 8 |
106 | (when (equal? datatype "@json") |
107 | (set! value (scm->json-string (canonical-json value))) |
108 | (set! datatype (rdf-iri "JSON"))) |
109 | (cond |
110 | ;; 9 |
111 | ((boolean? value) |
112 | (when (json-null? datatype) |
113 | (set! datatype (xsd-iri "boolean"))) |
114 | (set! value (if value "true" "false"))) |
115 | ;; 10 |
116 | ((and (number? value) |
117 | (or (not (integer? (inexact->exact value))) |
118 | (>= (abs value) (expt 10 21)) |
119 | (equal? datatype (xsd-iri "double")))) |
120 | (when (json-null? datatype) |
121 | (set! datatype (xsd-iri "double"))) |
122 | (set! value (canonical-double value))) |
123 | ;; 11 |
124 | ((number? value) |
125 | (set! value (number->string (inexact->exact value))) |
126 | (when (json-null? datatype) |
127 | (set! datatype (xsd-iri "integer")))) |
128 | ;; 12 |
129 | ((json-null? datatype) |
130 | (set! datatype |
131 | (if (json-has-key? item "@language") |
132 | (rdf-iri "langString") |
133 | (xsd-iri "string"))))) |
134 | (if (and (json-has-key? item "@direction") (not (equal? rdf-direction #f))) |
135 | ;; 13 |
136 | (let* ((language (or (not-null-or-false (assoc-ref item "@language")) "")) |
137 | (language (string-downcase language))) |
138 | (if (equal? rdf-direction "i18n-datatype") |
139 | ;; 13.2 |
140 | (begin |
141 | (set! datatype |
142 | (string-append "https://www.w3.org/ns/i18n#" language "_" |
143 | (assoc-ref item "@direction"))) |
144 | (set! result |
145 | (make-rdf-literal value datatype #f))) |
146 | ;; 13.3 |
147 | (when (equal? rdf-direction "compound-literal") |
148 | (let ((literal (blank-node->rdf-blank-node |
149 | (generate-blank-node json-null)))) |
150 | ;; 13.3.2 |
151 | (set! list-triples |
152 | (cons |
153 | ;; XXX: the spec says "of the @value in item as object", |
154 | ;; but should it be "value" instead? |
155 | (make-rdf-triple literal (rdf-iri "value") |
156 | (make-rdf-literal |
157 | (assoc-ref item "@value") |
158 | (xsd-iri "string") |
159 | #f)) |
160 | list-triples)) |
161 | ;; 13.3.3 |
162 | (when (json-has-key? item "@language") |
163 | (set! list-triples |
164 | (cons |
165 | (make-rdf-triple literal (rdf-iri "language") |
166 | (make-rdf-literal |
167 | language |
168 | (xsd-iri "string") |
169 | #f)) |
170 | list-triples))) |
171 | (set! list-triples |
172 | (cons |
173 | (make-rdf-triple literal (rdf-iri "direction") |
174 | (make-rdf-literal |
175 | (assoc-ref item "@direction") |
176 | (xsd-iri "string") |
177 | #f)) |
178 | list-triples)) |
179 | (set! result literal))))) |
180 | ;; 14 |
181 | (set! result |
182 | (make-rdf-literal value datatype (assoc-ref item "@language"))))))))) |
183 | (when (blank-node? result) |
184 | (set! result (blank-node->rdf-blank-node result))) |
185 | `(("result" . ,result) |
186 | ("list-triples" . ,list-triples)))) |
187 |