-
Notifications
You must be signed in to change notification settings - Fork 27
/
to_rdf.rb
192 lines (175 loc) · 8.69 KB
/
to_rdf.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# -*- encoding: utf-8 -*-
# frozen_string_literal: true
require 'rdf'
require 'rdf/nquads'
require 'json/canonicalization'
module JSON::LD
module ToRDF
include Utils
##
# @param [Hash{String => Object}] item
# @param [RDF::Resource] graph_name
# @param [Boolean] quoted emitted triples are quoted triples.
# @yield statement
# @yieldparam [RDF::Statement] statement
# @return RDF::Resource the subject of this item
def item_to_rdf(item, graph_name: nil, quoted: false, &block)
# Just return value object as Term
return unless item
if value?(item)
value, datatype = item.fetch('@value'), item.fetch('@type', nil)
datatype = RDF::URI(RDF.to_uri + "JSON") if datatype == '@json'
case value
when TrueClass, FalseClass
# If value is true or false, then set value its canonical lexical form as defined in the section Data Round Tripping. If datatype is null, set it to xsd:boolean.
value = value.to_s
datatype ||= RDF::XSD.boolean.to_s
when Numeric
# Otherwise, if value is a number, then set value to its canonical lexical form as defined in the section Data Round Tripping. If datatype is null, set it to either xsd:integer or xsd:double, depending on if the value contains a fractional and/or an exponential component.
value = if datatype == RDF::URI(RDF.to_uri + "JSON")
value.to_json_c14n
else
# Don't serialize as double if there are no fractional bits
as_double = value.ceil != value || value >= 1e21 || datatype == RDF::XSD.double
lit = if as_double
RDF::Literal::Double.new(value, canonicalize: true)
else
RDF::Literal.new(value.numerator, canonicalize: true)
end
datatype ||= lit.datatype
lit.to_s.sub("E+", "E")
end
when Array, Hash
# Only valid for rdf:JSON
value = value.to_json_c14n
else
if item.key?('@direction') && @options[:rdfDirection]
# Either serialize using a datatype, or a compound-literal
case @options[:rdfDirection]
when 'i18n-datatype'
datatype = RDF::URI("https://www.w3.org/ns/i18n##{item.fetch('@language', '').downcase}_#{item['@direction']}")
when 'compound-literal'
cl = RDF::Node.new
yield RDF::Statement(cl, RDF.value, item['@value'].to_s)
yield RDF::Statement(cl, RDF.to_uri + 'language', item['@language'].downcase) if item['@language']
yield RDF::Statement(cl, RDF.to_uri + 'direction', item['@direction'])
return cl
end
end
# Otherwise, if datatype is null, set it to xsd:string or xsd:langString, depending on if item has a @language key.
datatype ||= item.key?('@language') ? RDF.langString : RDF::XSD.string
if datatype == RDF::URI(RDF.to_uri + "JSON")
value = value.to_json_c14n
end
end
datatype = RDF::URI(datatype) if datatype && !datatype.is_a?(RDF::URI)
# Initialize literal as an RDF literal using value and datatype. If element has the key @language and datatype is xsd:string, then add the value associated with the @language key as the language of the object.
language = item.fetch('@language', nil) if datatype == RDF.langString
return RDF::Literal.new(value, datatype: datatype, language: language)
elsif list?(item)
# If item is a list object, initialize list_results as an empty array, and object to the result of the List Conversion algorithm, passing the value associated with the @list key from item and list_results.
return parse_list(item['@list'], graph_name: graph_name, &block)
end
subject = case item['@id']
when nil then node
when String then as_resource(item['@id'])
when Object
# Embedded/quoted statement
# (No error checking, as this is done in expansion)
to_enum(:item_to_rdf, item['@id'], quoted: true).to_a.first
end
#log_debug("item_to_rdf") {"subject: #{subject.to_ntriples rescue 'malformed rdf'}"}
item.each do |property, values|
case property
when '@type'
# If property is @type, construct triple as an RDF Triple composed of id, rdf:type, and object from values where id and object are represented either as IRIs or Blank Nodes
values.each do |v|
object = as_resource(v)
#log_debug("item_to_rdf") {"type: #{object.to_ntriples rescue 'malformed rdf'}"}
yield RDF::Statement(subject, RDF.type, object, graph_name: graph_name, quoted: quoted)
end
when '@graph'
values = [values].compact unless values.is_a?(Array)
values.each do |nd|
item_to_rdf(nd, graph_name: subject, quoted: quoted, &block)
end
when '@reverse'
raise "Huh?" unless values.is_a?(Hash)
values.each do |prop, vv|
predicate = as_resource(prop)
#log_debug("item_to_rdf") {"@reverse predicate: #{predicate.to_ntriples rescue 'malformed rdf'}"}
# For each item in values
vv.each do |v|
# Item is a node definition. Generate object as the result of the Object Converstion algorithm passing item.
object = item_to_rdf(v, graph_name: graph_name, &block)
#log_debug("item_to_rdf") {"subject: #{object.to_ntriples rescue 'malformed rdf'}"}
# yield subject, prediate, and literal to results.
yield RDF::Statement(object, predicate, subject, graph_name: graph_name, quoted: quoted)
end
end
when '@included'
values.each do |v|
item_to_rdf(v, graph_name: graph_name, &block)
end
when /^@/
# Otherwise, if @type is any other keyword, skip to the next property-values pair
else
# Otherwise, property is an IRI or Blank Node identifier
# Initialize predicate from property as an IRI or Blank node
predicate = as_resource(property)
#log_debug("item_to_rdf") {"predicate: #{predicate.to_ntriples rescue 'malformed rdf'}"}
# For each item in values
values.each do |v|
if list?(v)
#log_debug("item_to_rdf") {"list: #{v.inspect}"}
# If item is a list object, initialize list_results as an empty array, and object to the result of the List Conversion algorithm, passing the value associated with the @list key from item and list_results.
object = parse_list(v['@list'], graph_name: graph_name, &block)
# Append a triple composed of subject, prediate, and object to results and add all triples from list_results to results.
yield RDF::Statement(subject, predicate, object, graph_name: graph_name, quoted: quoted)
else
# Otherwise, item is a value object or a node definition. Generate object as the result of the Object Converstion algorithm passing item.
object = item_to_rdf(v, graph_name: graph_name, &block)
#log_debug("item_to_rdf") {"object: #{object.to_ntriples rescue 'malformed rdf'}"}
# yield subject, prediate, and literal to results.
yield RDF::Statement(subject, predicate, object, graph_name: graph_name, quoted: quoted)
end
end
end
end
subject
end
##
# Parse a List
#
# @param [Array] list
# The Array to serialize as a list
# @yield statement
# @yieldparam [RDF::Resource] statement
# @return [Array<RDF::Statement>]
# Statements for each item in the list
def parse_list(list, graph_name: nil, &block)
#log_debug('parse_list') {"list: #{list.inspect}"}
last = list.pop
result = first_bnode = last ? node : RDF.nil
list.each do |list_item|
# Set first to the result of the Object Converstion algorithm passing item.
object = item_to_rdf(list_item, graph_name: graph_name, &block)
yield RDF::Statement(first_bnode, RDF.first, object, graph_name: graph_name)
rest_bnode = node
yield RDF::Statement(first_bnode, RDF.rest, rest_bnode, graph_name: graph_name)
first_bnode = rest_bnode
end
if last
object = item_to_rdf(last, graph_name: graph_name, &block)
yield RDF::Statement(first_bnode, RDF.first, object, graph_name: graph_name)
yield RDF::Statement(first_bnode, RDF.rest, RDF.nil, graph_name: graph_name)
end
result
end
##
# Create a new named node using the sequence
def node
RDF::Node.new(namer.get_sym)
end
end
end