rdf-rdfxml 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +20 -0
- data/VERSION +1 -1
- data/lib/rdf/rdfxml/format.rb +2 -2
- data/lib/rdf/rdfxml/patches/graph_properties.rb +34 -0
- data/lib/rdf/rdfxml/patches/literal_hacks.rb +66 -0
- data/lib/rdf/rdfxml/patches/qname_hacks.rb +57 -0
- data/lib/rdf/rdfxml/patches/seq.rb +34 -0
- data/lib/rdf/rdfxml/patches/uri_hacks.rb +19 -0
- data/lib/rdf/rdfxml/reader.rb +10 -7
- data/lib/rdf/rdfxml/writer.rb +215 -114
- data/lib/rdf/rdfxml.rb +4 -0
- data/rdf-rdfxml.gemspec +17 -4
- data/spec/format_spec.rb +1 -1
- data/spec/graph_spec.rb +74 -0
- data/spec/literal_spec.rb +86 -0
- data/spec/rdf_escape_spec.rb +36 -0
- data/spec/rdf_helper.rb +2 -0
- data/spec/reader_spec.rb +7 -3
- data/spec/uri_spec.rb +61 -0
- data/spec/{xml_serializer_spec.rb → writer_spec.rb} +114 -110
- metadata +19 -6
data/lib/rdf/rdfxml/writer.rb
CHANGED
@@ -1,36 +1,84 @@
|
|
1
1
|
require 'nokogiri' # FIXME: Implement using different modules as in RDF::TriX
|
2
|
+
require 'rdf/rdfxml/patches/graph_properties'
|
2
3
|
|
3
4
|
module RDF::RDFXML
|
4
5
|
##
|
5
6
|
# An RDF/XML serialiser in Ruby
|
6
7
|
#
|
8
|
+
# Note that the natural interface is to write a whole graph at a time.
|
9
|
+
# Writing statements or Triples will create a graph to add them to
|
10
|
+
# and then serialize the graph.
|
11
|
+
#
|
12
|
+
# @example Obtaining a RDF/XML writer class
|
13
|
+
# RDF::Writer.for(:rdf) #=> RDF::TriX::Writer
|
14
|
+
# RDF::Writer.for("etc/test.rdf")
|
15
|
+
# RDF::Writer.for(:file_name => "etc/test.rdf")
|
16
|
+
# RDF::Writer.for(:file_extension => "rdf")
|
17
|
+
# RDF::Writer.for(:content_type => "application/rdf+xml")
|
18
|
+
#
|
19
|
+
# @example Serializing RDF graph into an RDF/XML file
|
20
|
+
# RDF::RDFXML::Write.open("etc/test.rdf") do |writer|
|
21
|
+
# writer.write_graph(graph)
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# @example Serializing RDF statements into an RDF/XML file
|
25
|
+
# RDF::RDFXML::Writer.open("etc/test.rdf") do |writer|
|
26
|
+
# graph.each_statement do |statement|
|
27
|
+
# writer << statement
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# @example Serializing RDF statements into an RDF/XML string
|
32
|
+
# RDF::RDFXML::Writer.buffer do |writer|
|
33
|
+
# graph.each_statement do |statement|
|
34
|
+
# writer << statement
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
7
38
|
# @author [Gregg Kellogg](http://kellogg-assoc.com/)
|
8
39
|
class Writer < RDF::Writer
|
9
40
|
format RDF::RDFXML::Format
|
10
41
|
|
11
42
|
VALID_ATTRIBUTES = [:none, :untyped, :typed]
|
12
43
|
|
13
|
-
attr_accessor :graph, :
|
14
|
-
|
44
|
+
attr_accessor :graph, :base_uri
|
15
45
|
|
16
46
|
##
|
47
|
+
# Initializes the RDF/XML writer instance.
|
48
|
+
#
|
49
|
+
# Opitons:
|
50
|
+
# max_depth:: Maximum depth for recursively defining resources, defaults to 3
|
51
|
+
# base_uri:: Base URI of graph, used to shorting URI references
|
52
|
+
# lang:: Output as root xml:lang attribute, and avoid generation _xml:lang_ where possible
|
53
|
+
# attributes:: How to use XML attributes when serializing, one of :none, :untyped, :typed. The default is :none.
|
54
|
+
# defafult_ns:: URI to use as default namespace
|
55
|
+
#
|
17
56
|
# @param [IO, File] output
|
18
57
|
# @param [Hash{Symbol => Object}] options
|
58
|
+
# @option options [Integer] :max_depth (nil)
|
59
|
+
# @option options [String, #to_s] :base_uri (nil)
|
60
|
+
# @option options [String, #to_s] :lang (nil)
|
61
|
+
# @option options [Array] :attributes (nil)
|
62
|
+
# @option options [String] :default_namespace
|
19
63
|
# @yield [writer]
|
20
64
|
# @yieldparam [RDF::Writer] writer
|
21
65
|
def initialize(output = $stdout, options = {}, &block)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
66
|
+
@graph = RDF::Graph.new
|
67
|
+
super
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# @param [Graph] graph
|
72
|
+
# @return [void]
|
73
|
+
def write_graph(graph)
|
74
|
+
@graph = graph
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# @param [Statement] statement
|
79
|
+
# @return [void]
|
80
|
+
def write_statement(statement)
|
81
|
+
@graph << statement
|
34
82
|
end
|
35
83
|
|
36
84
|
##
|
@@ -42,7 +90,7 @@ module RDF::RDFXML
|
|
42
90
|
# @return [void]
|
43
91
|
# @see #write_epilogue
|
44
92
|
def write_triple(subject, predicate, object)
|
45
|
-
@graph <<
|
93
|
+
@graph << RDF::Statement.new(subject, predicate, object)
|
46
94
|
end
|
47
95
|
|
48
96
|
##
|
@@ -51,64 +99,59 @@ module RDF::RDFXML
|
|
51
99
|
# @return [void]
|
52
100
|
# @see #write_triple
|
53
101
|
def write_epilogue
|
102
|
+
@base_uri = nil
|
103
|
+
@force_RDF_about = {}
|
104
|
+
@max_depth = @options[:max_depth] || 3
|
105
|
+
@base_uri = @options[:base_uri]
|
106
|
+
@lang = @options[:lang]
|
107
|
+
@attributes = @options[:attributes] || :none
|
108
|
+
@debug = @options[:debug]
|
109
|
+
@default_namespace = @options[:default_namespace]
|
110
|
+
raise "Invalid attribute option '#{@attributes}', should be one of #{VALID_ATTRIBUTES.to_sentence}" unless VALID_ATTRIBUTES.include?(@attributes.to_sym)
|
111
|
+
self.reset
|
112
|
+
|
54
113
|
doc = Nokogiri::XML::Document.new
|
55
114
|
|
56
|
-
|
115
|
+
add_debug "\nserialize: graph namespaces: #{@namespaces.inspect}"
|
116
|
+
add_debug "\nserialize: graph: #{@graph.size}"
|
57
117
|
|
58
118
|
preprocess
|
59
119
|
|
60
120
|
predicates = @graph.predicates.uniq
|
121
|
+
add_debug "\nserialize: predicates #{predicates.inspect}"
|
61
122
|
possible = predicates + @graph.objects.uniq
|
62
123
|
namespaces = {}
|
63
124
|
required_namespaces = {}
|
64
125
|
possible.each do |res|
|
65
|
-
|
66
|
-
if res.namespace
|
67
|
-
add_namespace(res.namespace)
|
68
|
-
else
|
69
|
-
required_namespaces[res.base] = true
|
70
|
-
end
|
71
|
-
#puts "possible namespace for #{res}: #{res.namespace || %(<#{res.base}>)}"
|
126
|
+
get_qname(res)
|
72
127
|
end
|
73
|
-
add_namespace(RDF_NS)
|
74
|
-
add_namespace(
|
128
|
+
add_namespace(:rdf, RDF_NS)
|
129
|
+
add_namespace(:xml, RDF::XML) if @base_uri || @lang
|
75
130
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
add_namespace(@default_ns)
|
81
|
-
prefix = @graph.prefix(@default_ns.uri)
|
82
|
-
@prefixed_default_ns = @graph.namespace(prefix)
|
83
|
-
add_namespace(@prefixed_default_ns) if @prefixed_default_ns
|
131
|
+
if @default_namespace
|
132
|
+
add_namespace("", @default_namespace)
|
133
|
+
@default_namespace_prefix = @namespaces.invert[@default_namespace]
|
134
|
+
add_debug("def_namespace: #{@default_namespace}, prefix: #{@default_namespace_prefix}")
|
84
135
|
end
|
85
136
|
|
86
|
-
# Add bindings for predicates not already having bindings
|
87
|
-
tmp_ns = "ns0"
|
88
|
-
required_namespaces.keys.each do |uri|
|
89
|
-
puts "create namespace definition for #{uri}" if $DEBUG
|
90
|
-
add_namespace(Namespace.new(uri, tmp_ns))
|
91
|
-
tmp_ns = tmp_ns.succ
|
92
|
-
end
|
93
|
-
|
94
137
|
doc.root = Nokogiri::XML::Element.new("rdf:RDF", doc)
|
95
|
-
@namespaces.each_pair do |p,
|
138
|
+
@namespaces.each_pair do |p, uri|
|
96
139
|
if p.to_s.empty?
|
97
|
-
doc.root.default_namespace =
|
140
|
+
doc.root.default_namespace = uri.to_s
|
98
141
|
else
|
99
|
-
doc.root.add_namespace(p,
|
142
|
+
doc.root.add_namespace(p.to_s, uri.to_s)
|
100
143
|
end
|
101
144
|
end
|
102
145
|
doc.root["xml:lang"] = @lang if @lang
|
103
|
-
doc.root["xml:base"] = @
|
146
|
+
doc.root["xml:base"] = @base_uri if @base_uri
|
104
147
|
|
105
148
|
# Add statements for each subject
|
106
149
|
order_subjects.each do |subject|
|
107
|
-
#
|
150
|
+
#add_debug "subj: #{subject.inspect}"
|
108
151
|
subject(subject, doc.root)
|
109
152
|
end
|
110
153
|
|
111
|
-
doc.write_xml_to(
|
154
|
+
doc.write_xml_to(@output, :encoding => "UTF-8", :indent => 2)
|
112
155
|
end
|
113
156
|
|
114
157
|
protected
|
@@ -119,23 +162,25 @@ module RDF::RDFXML
|
|
119
162
|
subject_done(subject)
|
120
163
|
properties = @graph.properties(subject)
|
121
164
|
prop_list = sort_properties(properties)
|
122
|
-
|
165
|
+
add_debug "subject: #{subject.inspect}, props: #{properties.inspect}"
|
123
166
|
|
124
|
-
rdf_type, *rest = properties.fetch(
|
167
|
+
rdf_type, *rest = properties.fetch(RDF.type.to_s, [])
|
125
168
|
if rdf_type.is_a?(RDF::URI)
|
126
169
|
element = get_qname(rdf_type)
|
127
|
-
properties[
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
170
|
+
properties[RDF.type.to_s] = rest
|
171
|
+
|
172
|
+
# FIXME: different namespace logic
|
173
|
+
type_ns = rdf_type.vocab rescue nil
|
174
|
+
if type_ns && @default_namespace && type_ns.to_s == @default_namespace.to_s
|
175
|
+
properties[RDF.type.to_s] = rest
|
176
|
+
element = rdf_type.qname.last
|
132
177
|
end
|
133
178
|
end
|
134
179
|
element ||= "rdf:Description"
|
135
180
|
|
136
181
|
node = Nokogiri::XML::Element.new(element, parent_node.document)
|
137
182
|
|
138
|
-
if subject.is_a?(
|
183
|
+
if subject.is_a?(RDF::Node)
|
139
184
|
# Only need nodeID if it's referenced elsewhere
|
140
185
|
node["rdf:nodeID"] = subject.to_s if ref_count(subject) > (@depth == 0 ? 0 : 1)
|
141
186
|
else
|
@@ -152,7 +197,7 @@ module RDF::RDFXML
|
|
152
197
|
end
|
153
198
|
end
|
154
199
|
elsif @force_RDF_about.include?(subject)
|
155
|
-
|
200
|
+
add_debug "subject: #{subject.inspect}, force about"
|
156
201
|
node = Nokogiri::XML::Element.new("rdf:Description", parent_node.document)
|
157
202
|
node["rdf:about"] = relativize(subject)
|
158
203
|
@force_RDF_about.delete(subject)
|
@@ -165,7 +210,7 @@ module RDF::RDFXML
|
|
165
210
|
#
|
166
211
|
# If _is_unique_ is true, this predicate may be able to be serialized as an attribute
|
167
212
|
def predicate(prop, object, node, is_unique)
|
168
|
-
qname = prop
|
213
|
+
qname = get_qname(prop)
|
169
214
|
raise RdfException, "No qname generated for <#{prop}>" unless qname
|
170
215
|
|
171
216
|
# See if we can serialize as attribute.
|
@@ -173,72 +218,77 @@ module RDF::RDFXML
|
|
173
218
|
# * typed attributes that aren't duplicated if @dt_as_attr is true
|
174
219
|
# * rdf:type
|
175
220
|
as_attr = false
|
176
|
-
as_attr ||= true if [:untyped, :typed].include?(@attributes) && prop ==
|
221
|
+
as_attr ||= true if [:untyped, :typed].include?(@attributes) && prop == RDF.type
|
177
222
|
|
178
|
-
# Untyped attribute with no lang, or whos lang is the same as the default and
|
223
|
+
# Untyped attribute with no lang, or whos lang is the same as the default and RDF.type
|
224
|
+
add_debug("as_attr? #{@attributes}, plain? #{object.plain?}, lang #{@lang || 'nil'}:#{object.language || 'nil'}") if object.is_a?(RDF::Literal)
|
179
225
|
as_attr ||= true if [:untyped, :typed].include?(@attributes) &&
|
180
|
-
|
226
|
+
object.is_a?(RDF::Literal) && (object.plain? || (@lang && object.language.to_s == @lang.to_s))
|
181
227
|
|
182
|
-
as_attr ||= true if [:typed].include?(@attributes) && object.is_a?(Literal) && object.typed?
|
228
|
+
as_attr ||= true if [:typed].include?(@attributes) && object.is_a?(RDF::Literal) && object.typed?
|
183
229
|
|
184
230
|
as_attr = false unless is_unique
|
185
231
|
|
186
232
|
# Can't do as an attr if the qname has no prefix and there is no prefixed version
|
187
|
-
if @
|
233
|
+
if @default_namespace && prop.vocab.to_s == @default_namespace.to_s
|
188
234
|
if as_attr
|
189
|
-
if @
|
190
|
-
qname = "#{@
|
235
|
+
if @default_namespace_prefix
|
236
|
+
qname = "#{@default_namespace_prefix}:#{prop.qname.last}"
|
191
237
|
else
|
192
238
|
as_attr = false
|
193
239
|
end
|
194
240
|
else
|
195
|
-
qname = prop.
|
241
|
+
qname = prop.qname.last
|
196
242
|
end
|
197
243
|
end
|
198
244
|
|
199
|
-
|
245
|
+
add_debug "predicate: #{qname}, as_attr: #{as_attr}, object: #{object.inspect}, done: #{is_done?(object)}, sub: #{@subjects.include?(object)}"
|
200
246
|
qname = "rdf:li" if qname.match(/rdf:_\d+/)
|
201
247
|
pred_node = Nokogiri::XML::Element.new(qname, node.document)
|
202
248
|
|
203
|
-
if object.is_a?(Literal) || is_done?(object) || !@subjects.include?(object)
|
249
|
+
if object.is_a?(RDF::Literal) || is_done?(object) || !@subjects.include?(object)
|
204
250
|
# Literals or references to objects that aren't subjects, or that have already been serialized
|
205
251
|
|
206
|
-
args = object
|
207
|
-
|
252
|
+
args = xml_args(object)
|
253
|
+
add_debug "predicate: args=#{args.inspect}"
|
208
254
|
attrs = args.pop
|
209
255
|
|
210
256
|
if as_attr
|
211
257
|
# Serialize as attribute
|
212
258
|
pred_node.unlink
|
213
259
|
pred_node = nil
|
214
|
-
node[qname] = object.is_a?(RDF::URI) ? relativize(object) : object.
|
260
|
+
node[qname] = object.is_a?(RDF::URI) ? relativize(object) : object.value
|
261
|
+
add_debug("node[#{qname}]=#{node[qname]}, #{object.class}")
|
215
262
|
else
|
216
263
|
# Serialize as element
|
264
|
+
add_debug("serialize as element: #{attrs.inspect}")
|
217
265
|
attrs.each_pair do |a, av|
|
218
|
-
next if a == "xml:lang" && av == @lang # Lang already specified, don't repeat
|
219
|
-
av = relativize(object) if a == "
|
220
|
-
|
266
|
+
next if a.to_s == "xml:lang" && av.to_s == @lang # Lang already specified, don't repeat
|
267
|
+
av = relativize(object) if a == "rdf:resource"
|
268
|
+
add_debug " elt attr #{a}=#{av}"
|
221
269
|
pred_node[a] = av.to_s
|
222
270
|
end
|
223
|
-
|
224
|
-
if object.is_a?(Literal) && object.xmlliteral?
|
271
|
+
add_debug " elt #{'xmllit ' if object.is_a?(RDF::Literal) && object.xmlliteral?}content=#{args.first}" if !args.empty?
|
272
|
+
if object.is_a?(RDF::Literal) && object.xmlliteral?
|
225
273
|
pred_node.add_child(Nokogiri::XML::CharacterData.new(args.first, node.document))
|
226
274
|
elsif args.first
|
227
275
|
pred_node.content = args.first unless args.empty?
|
228
276
|
end
|
229
277
|
end
|
230
278
|
else
|
279
|
+
require 'rdf/rdfxml/patches/seq' unless RDF::Graph.respond_to?(:seq)
|
280
|
+
|
231
281
|
# Check to see if it can be serialized as a collection
|
232
282
|
col = @graph.seq(object)
|
233
|
-
conformant_list = col.all? {|item| !item.is_a?(Literal)}
|
283
|
+
conformant_list = col.all? {|item| !item.is_a?(RDF::Literal)}
|
234
284
|
o_props = @graph.properties(object)
|
235
|
-
if conformant_list && o_props[
|
285
|
+
if conformant_list && o_props[RDF.first.to_s]
|
236
286
|
# Serialize list as parseType="Collection"
|
237
287
|
pred_node["rdf:parseType"] = "Collection"
|
238
288
|
col.each do |item|
|
239
289
|
# Mark the BNode subject of each item as being complete, so that it is not serialized
|
240
|
-
@graph.
|
241
|
-
subject_done(
|
290
|
+
@graph.query(:predicate => RDF.first, :object => item) do |statement|
|
291
|
+
subject_done(statement.subject)
|
242
292
|
end
|
243
293
|
@force_RDF_about[item] = true
|
244
294
|
subject(item, pred_node)
|
@@ -248,7 +298,7 @@ module RDF::RDFXML
|
|
248
298
|
@depth += 1
|
249
299
|
subject(object, pred_node)
|
250
300
|
@depth -= 1
|
251
|
-
elsif object.is_a?(
|
301
|
+
elsif object.is_a?(RDF::Node)
|
252
302
|
pred_node["rdf:nodeID"] = object.identifier
|
253
303
|
else
|
254
304
|
pred_node["rdf:resource"] = relativize(object)
|
@@ -260,7 +310,7 @@ module RDF::RDFXML
|
|
260
310
|
|
261
311
|
def relativize(uri)
|
262
312
|
uri = uri.to_s
|
263
|
-
self.
|
313
|
+
self.base_uri ? uri.sub(/^#{self.base_uri}/, "") : uri
|
264
314
|
end
|
265
315
|
|
266
316
|
def preprocess_triple(triple)
|
@@ -268,7 +318,7 @@ module RDF::RDFXML
|
|
268
318
|
|
269
319
|
# Pre-fetch qnames, to fill namespaces
|
270
320
|
get_qname(triple.predicate)
|
271
|
-
get_qname(triple.object) if triple.predicate ==
|
321
|
+
get_qname(triple.object) if triple.predicate == RDF.type
|
272
322
|
|
273
323
|
@references[triple.predicate] = ref_count(triple.predicate) + 1
|
274
324
|
end
|
@@ -276,8 +326,8 @@ module RDF::RDFXML
|
|
276
326
|
MAX_DEPTH = 10
|
277
327
|
INDENT_STRING = " "
|
278
328
|
|
279
|
-
def top_classes; [
|
280
|
-
def predicate_order; [
|
329
|
+
def top_classes; [RDF::RDFS.Class]; end
|
330
|
+
def predicate_order; [RDF.type, RDF::RDFS.label, RDF::DC.title]; end
|
281
331
|
|
282
332
|
def is_done?(subject)
|
283
333
|
@serialized.include?(subject)
|
@@ -293,8 +343,8 @@ module RDF::RDFXML
|
|
293
343
|
subjects = []
|
294
344
|
|
295
345
|
top_classes.each do |class_uri|
|
296
|
-
graph.
|
297
|
-
#
|
346
|
+
graph.query(:predicate => RDF.type, :object => class_uri).map {|st| st.subject}.sort.uniq.each do |subject|
|
347
|
+
#add_debug "order_subjects: #{subject.inspect}"
|
298
348
|
subjects << subject
|
299
349
|
seen[subject] = @top_levels[subject] = true
|
300
350
|
end
|
@@ -303,21 +353,21 @@ module RDF::RDFXML
|
|
303
353
|
# Sort subjects by resources over bnodes, ref_counts and the subject URI itself
|
304
354
|
recursable = @subjects.keys.
|
305
355
|
select {|s| !seen.include?(s)}.
|
306
|
-
map {|r| [r.is_a?(
|
356
|
+
map {|r| [r.is_a?(RDF::Node) ? 1 : 0, ref_count(r), r]}.
|
307
357
|
sort
|
308
358
|
|
309
359
|
subjects += recursable.map{|r| r.last}
|
310
360
|
end
|
311
361
|
|
312
362
|
def preprocess
|
313
|
-
@graph.
|
363
|
+
@graph.each {|statement| preprocess_statement(statement)}
|
314
364
|
end
|
315
365
|
|
316
|
-
def
|
317
|
-
#
|
318
|
-
references = ref_count(
|
319
|
-
@references[
|
320
|
-
@subjects[
|
366
|
+
def preprocess_statement(statement)
|
367
|
+
#add_debug "preprocess: #{statement.inspect}"
|
368
|
+
references = ref_count(statement.object) + 1
|
369
|
+
@references[statement.object] = references
|
370
|
+
@subjects[statement.subject] = true
|
321
371
|
end
|
322
372
|
|
323
373
|
# Return the number of times this node has been referenced in the object position
|
@@ -328,22 +378,51 @@ module RDF::RDFXML
|
|
328
378
|
# Return a QName for the URI, or nil. Adds namespace of QName to defined namespaces
|
329
379
|
def get_qname(uri)
|
330
380
|
if uri.is_a?(RDF::URI)
|
331
|
-
|
332
|
-
|
333
|
-
|
381
|
+
# Duplicate logic from URI#qname to remember namespace assigned
|
382
|
+
if uri.qname
|
383
|
+
add_namespace(uri.qname.first, uri.vocab)
|
384
|
+
add_debug "get_qname(uri.qname): #{uri.qname.join(':')}"
|
385
|
+
return uri.qname.join(":")
|
386
|
+
end
|
334
387
|
|
335
|
-
|
336
|
-
|
388
|
+
# No vocabulary assigned, find one from cache of created namespace URIs
|
389
|
+
@namespaces.each_pair do |prefix, vocab|
|
390
|
+
if uri.to_s.index(vocab.to_s) == 0
|
391
|
+
uri.vocab = vocab
|
392
|
+
local_name = uri.to_s[(vocab.to_s.length)..-1]
|
393
|
+
add_debug "get_qname(ns): #{prefix}:#{local_name}"
|
394
|
+
return "#{prefix}:#{local_name}"
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
# No vocabulary found, invent one
|
399
|
+
# Add bindings for predicates not already having bindings
|
400
|
+
# short_name of URI for creating QNames.
|
401
|
+
# "#{base_uri]{#short_name}}" == uri
|
402
|
+
local_name = uri.fragment
|
403
|
+
local_name ||= begin
|
404
|
+
path = uri.path.split("/")
|
405
|
+
unless path &&
|
406
|
+
path.length > 1 &&
|
407
|
+
path.last.class == String &&
|
408
|
+
path.last.length > 0 &&
|
409
|
+
path.last.index("/") != 0
|
410
|
+
return false
|
411
|
+
end
|
412
|
+
path.last
|
413
|
+
end
|
414
|
+
base_uri = uri.to_s[0..-(local_name.length + 1)]
|
415
|
+
@tmp_ns = @tmp_ns ? @tmp_ns.succ : "ns0"
|
416
|
+
add_debug "create namespace definition for #{uri}"
|
417
|
+
uri.vocab = RDF::Vocabulary.new(base_uri)
|
418
|
+
add_namespace(@tmp_ns.to_sym, uri.vocab)
|
419
|
+
add_debug "get_qname(tmp_ns): #{@tmp_ns}:#{local_name}"
|
420
|
+
return "#{@tmp_ns}:#{local_name}"
|
337
421
|
end
|
338
422
|
end
|
339
423
|
|
340
|
-
def add_namespace(ns)
|
341
|
-
@namespaces[
|
342
|
-
end
|
343
|
-
|
344
|
-
# URI -> Namespace bindings (similar to graph) for looking up qnames
|
345
|
-
def uri_binding
|
346
|
-
@uri_binding ||= @namespaces.values.inject({}) {|hash, ns| hash[ns.uri.to_s] = ns; hash}
|
424
|
+
def add_namespace(prefix, ns)
|
425
|
+
@namespaces[prefix.to_sym] = ns.to_s
|
347
426
|
end
|
348
427
|
|
349
428
|
def reset
|
@@ -361,8 +440,8 @@ module RDF::RDFXML
|
|
361
440
|
def sort_properties(properties)
|
362
441
|
properties.keys.each do |k|
|
363
442
|
properties[k] = properties[k].sort do |a, b|
|
364
|
-
a_li = a.is_a?(RDF::URI) && a.
|
365
|
-
b_li = b.is_a?(RDF::URI) && b.
|
443
|
+
a_li = a.is_a?(RDF::URI) && a.qname.last =~ /^_\d+$/ ? a.to_i : a.to_s
|
444
|
+
b_li = b.is_a?(RDF::URI) && b.qname.last =~ /^_\d+$/ ? b.to_i : b.to_s
|
366
445
|
|
367
446
|
a_li <=> b_li
|
368
447
|
end
|
@@ -381,19 +460,41 @@ module RDF::RDFXML
|
|
381
460
|
prop_list << prop.to_s
|
382
461
|
end
|
383
462
|
|
384
|
-
|
463
|
+
add_debug "sort_properties: #{prop_list.to_sentence}"
|
385
464
|
prop_list
|
386
465
|
end
|
387
466
|
|
388
|
-
#
|
389
|
-
|
390
|
-
|
467
|
+
# XML content and arguments for serialization
|
468
|
+
# Encoding.the_null_encoding.xml_args("foo", "en-US") => ["foo", {"xml:lang" => "en-US"}]
|
469
|
+
def xml_args(object)
|
470
|
+
case object
|
471
|
+
when RDF::Literal
|
472
|
+
if object.plain?
|
473
|
+
[object.value, {}]
|
474
|
+
elsif object.has_language?
|
475
|
+
[object.value, {"xml:lang" => object.language}]
|
476
|
+
elsif object.xmlliteral?
|
477
|
+
[object.value, {"rdf:parseType" => "Literal"}]
|
478
|
+
else
|
479
|
+
[object.value, {"rdf:datatype" => object.datatype.to_s}]
|
480
|
+
end
|
481
|
+
when RDF::Node
|
482
|
+
[{"rdf:nodeID" => object.id}]
|
483
|
+
when RDF::URI
|
484
|
+
[{"rdf:resource" => object.to_s}]
|
485
|
+
end
|
391
486
|
end
|
392
487
|
|
393
|
-
#
|
394
|
-
|
395
|
-
|
488
|
+
# Add debug event to debug array, if specified
|
489
|
+
#
|
490
|
+
# @param [String] message::
|
491
|
+
def add_debug(message)
|
492
|
+
@debug << message if @debug.is_a?(Array)
|
396
493
|
end
|
397
494
|
|
495
|
+
# Returns indent string multiplied by the depth
|
496
|
+
def indent(modifier = 0)
|
497
|
+
INDENT_STRING * (@depth + modifier)
|
498
|
+
end
|
398
499
|
end
|
399
500
|
end
|
data/lib/rdf/rdfxml.rb
CHANGED
@@ -23,8 +23,11 @@ module RDF
|
|
23
23
|
require 'rdf/rdfxml/format'
|
24
24
|
require 'rdf/rdfxml/vocab'
|
25
25
|
require 'rdf/rdfxml/patches/array_hacks'
|
26
|
+
require 'rdf/rdfxml/patches/literal_hacks'
|
26
27
|
require 'rdf/rdfxml/patches/nokogiri_hacks'
|
28
|
+
require 'rdf/rdfxml/patches/qname_hacks'
|
27
29
|
require 'rdf/rdfxml/patches/rdf_escape'
|
30
|
+
require 'rdf/rdfxml/patches/uri_hacks'
|
28
31
|
autoload :Reader, 'rdf/rdfxml/reader'
|
29
32
|
autoload :Writer, 'rdf/rdfxml/writer'
|
30
33
|
autoload :VERSION, 'rdf/rdfxml/version'
|
@@ -32,5 +35,6 @@ module RDF
|
|
32
35
|
|
33
36
|
# Fixme: RDF.to_s should generate this, but it doesn't
|
34
37
|
RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
38
|
+
XML_LITERAL = RDF['XMLLiteral']
|
35
39
|
end
|
36
40
|
end
|
data/rdf-rdfxml.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rdf-rdfxml}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Gregg Kellogg"]
|
12
|
-
s.date = %q{2010-06-
|
12
|
+
s.date = %q{2010-06-12}
|
13
13
|
s.description = %q{ RDF::RDFXML is an RDF/XML reader and writer for Ruby using the RDF.rb library suite.
|
14
14
|
}
|
15
15
|
s.email = %q{gregg@kellogg-assoc.com}
|
@@ -31,8 +31,13 @@ Gem::Specification.new do |s|
|
|
31
31
|
"lib/rdf/rdfxml.rb",
|
32
32
|
"lib/rdf/rdfxml/format.rb",
|
33
33
|
"lib/rdf/rdfxml/patches/array_hacks.rb",
|
34
|
+
"lib/rdf/rdfxml/patches/graph_properties.rb",
|
35
|
+
"lib/rdf/rdfxml/patches/literal_hacks.rb",
|
34
36
|
"lib/rdf/rdfxml/patches/nokogiri_hacks.rb",
|
37
|
+
"lib/rdf/rdfxml/patches/qname_hacks.rb",
|
35
38
|
"lib/rdf/rdfxml/patches/rdf_escape.rb",
|
39
|
+
"lib/rdf/rdfxml/patches/seq.rb",
|
40
|
+
"lib/rdf/rdfxml/patches/uri_hacks.rb",
|
36
41
|
"lib/rdf/rdfxml/reader.rb",
|
37
42
|
"lib/rdf/rdfxml/version.rb",
|
38
43
|
"lib/rdf/rdfxml/vocab.rb",
|
@@ -40,7 +45,10 @@ Gem::Specification.new do |s|
|
|
40
45
|
"rdf-rdfxml.gemspec",
|
41
46
|
"script/console",
|
42
47
|
"spec/format_spec.rb",
|
48
|
+
"spec/graph_spec.rb",
|
49
|
+
"spec/literal_spec.rb",
|
43
50
|
"spec/matchers.rb",
|
51
|
+
"spec/rdf_escape_spec.rb",
|
44
52
|
"spec/rdf_helper.rb",
|
45
53
|
"spec/rdf_tests/cc197bad-dc9c-440d-a5b5-d52ba2e14234.nt",
|
46
54
|
"spec/rdf_tests/cc197bad-dc9c-440d-a5b5-d52ba2e14234.rdf",
|
@@ -404,7 +412,8 @@ Gem::Specification.new do |s|
|
|
404
412
|
"spec/reader_spec.rb",
|
405
413
|
"spec/spec.opts",
|
406
414
|
"spec/spec_helper.rb",
|
407
|
-
"spec/
|
415
|
+
"spec/uri_spec.rb",
|
416
|
+
"spec/writer_spec.rb"
|
408
417
|
]
|
409
418
|
s.homepage = %q{http://github.com/gkellogg/rdf-rdfxml}
|
410
419
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -413,11 +422,15 @@ Gem::Specification.new do |s|
|
|
413
422
|
s.summary = %q{RDF/XML reader/writer for RDF.rb.}
|
414
423
|
s.test_files = [
|
415
424
|
"spec/format_spec.rb",
|
425
|
+
"spec/graph_spec.rb",
|
426
|
+
"spec/literal_spec.rb",
|
416
427
|
"spec/matchers.rb",
|
428
|
+
"spec/rdf_escape_spec.rb",
|
417
429
|
"spec/rdf_helper.rb",
|
418
430
|
"spec/reader_spec.rb",
|
419
431
|
"spec/spec_helper.rb",
|
420
|
-
"spec/
|
432
|
+
"spec/uri_spec.rb",
|
433
|
+
"spec/writer_spec.rb"
|
421
434
|
]
|
422
435
|
|
423
436
|
if s.respond_to? :specification_version then
|
data/spec/format_spec.rb
CHANGED
@@ -4,7 +4,7 @@ describe RDF::RDFXML::Format do
|
|
4
4
|
context "descovery" do
|
5
5
|
{
|
6
6
|
"rdf" => RDF::Format.for(:rdf),
|
7
|
-
"
|
7
|
+
"rdfxml" => RDF::Format.for(:rdfxml),
|
8
8
|
"etc/foaf.xml" => RDF::Format.for("etc/foaf.xml"),
|
9
9
|
"etc/foaf.rdf" => RDF::Format.for("etc/foaf.rdf"),
|
10
10
|
"foaf.xml" => RDF::Format.for(:file_name => "foaf.xml"),
|