guile-jsonld/jsonld/object-to-rdf.scm

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