the-experimenters-rdf-rdfxml 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ module RDF::RDFXML::VERSION
2
+ VERSION_FILE = File.join(File.expand_path(File.dirname(__FILE__)), "..", "..", "..", "VERSION")
3
+ MAJOR, MINOR, TINY, EXTRA = File.read(VERSION_FILE).chop.split(".")
4
+
5
+ STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.')
6
+
7
+ ##
8
+ # @return [String]
9
+ def self.to_s() STRING end
10
+
11
+ ##
12
+ # @return [String]
13
+ def self.to_str() STRING end
14
+
15
+ ##
16
+ # @return [Array(Integer, Integer, Integer)]
17
+ def self.to_a() STRING.split(".") end
18
+ end
@@ -0,0 +1,3 @@
1
+ module RDF
2
+ class XML < Vocabulary("http://www.w3.org/XML/1998/namespace"); end
3
+ end
@@ -0,0 +1,559 @@
1
+ require 'nokogiri' # FIXME: Implement using different modules as in RDF::TriX
2
+ require 'rdf/rdfxml/patches/graph_properties'
3
+
4
+ module RDF::RDFXML
5
+ ##
6
+ # An RDF/XML serialiser in Ruby
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
+ # The writer will add prefix definitions, and use them for creating @prefix definitions, and minting QNames
13
+ #
14
+ # @example Obtaining a RDF/XML writer class
15
+ # RDF::Writer.for(:rdf) #=> RDF::RDFXML::Writer
16
+ # RDF::Writer.for("etc/test.rdf")
17
+ # RDF::Writer.for(:file_name => "etc/test.rdf")
18
+ # RDF::Writer.for(:file_extension => "rdf")
19
+ # RDF::Writer.for(:content_type => "application/rdf+xml")
20
+ #
21
+ # @example Serializing RDF graph into an RDF/XML file
22
+ # RDF::RDFXML::Write.open("etc/test.rdf") do |writer|
23
+ # writer << graph
24
+ # end
25
+ #
26
+ # @example Serializing RDF statements into an RDF/XML file
27
+ # RDF::RDFXML::Writer.open("etc/test.rdf") do |writer|
28
+ # graph.each_statement do |statement|
29
+ # writer << statement
30
+ # end
31
+ # end
32
+ #
33
+ # @example Serializing RDF statements into an RDF/XML string
34
+ # RDF::RDFXML::Writer.buffer do |writer|
35
+ # graph.each_statement do |statement|
36
+ # writer << statement
37
+ # end
38
+ # end
39
+ #
40
+ # @example Creating @base and @prefix definitions in output
41
+ # RDF::RDFXML::Writer.buffer(:base_uri => "http://example.com/", :prefixes => {
42
+ # nil => "http://example.com/ns#",
43
+ # :foaf => "http://xmlns.com/foaf/0.1/"}
44
+ # ) do |writer|
45
+ # graph.each_statement do |statement|
46
+ # writer << statement
47
+ # end
48
+ # end
49
+ #
50
+ # @author [Gregg Kellogg](http://kellogg-assoc.com/)
51
+ class Writer < RDF::Writer
52
+ format RDF::RDFXML::Format
53
+
54
+ VALID_ATTRIBUTES = [:none, :untyped, :typed]
55
+
56
+ # @return [Graph] Graph of statements serialized
57
+ attr_accessor :graph
58
+ # @return [URI] Base URI used for relativizing URIs
59
+ attr_accessor :base_uri
60
+
61
+ ##
62
+ # Initializes the RDF/XML writer instance.
63
+ #
64
+ # @param [IO, File] output
65
+ # the output stream
66
+ # @param [Hash{Symbol => Object}] options
67
+ # any additional options
68
+ # @option options [Boolean] :canonicalize (false)
69
+ # whether to canonicalize literals when serializing
70
+ # @option options [Hash] :prefixes (Hash.new)
71
+ # the prefix mappings to use (not supported by all writers)
72
+ # @option options [#to_s] :base_uri (nil)
73
+ # the base URI to use when constructing relative URIs
74
+ # @option options [Integer] :max_depth (3)
75
+ # Maximum depth for recursively defining resources
76
+ # @option options [#to_s] :lang (nil)
77
+ # Output as root xml:lang attribute, and avoid generation _xml:lang_ where possible
78
+ # @option options [Array] :attributes (nil)
79
+ # How to use XML attributes when serializing, one of :none, :untyped, :typed. The default is :none.
80
+ # @option options [Boolean] :standard_prefixes (false)
81
+ # Add standard prefixes to _prefixes_, if necessary.
82
+ # @option options [String] :default_namespace (nil)
83
+ # URI to use as default namespace, same as prefix(nil)
84
+ # @yield [writer]
85
+ # @yieldparam [RDF::Writer] writer
86
+ def initialize(output = $stdout, options = {}, &block)
87
+ super do
88
+ @graph = RDF::Graph.new
89
+ @uri_to_qname = {}
90
+ @uri_to_prefix = {}
91
+ block.call(self) if block_given?
92
+ end
93
+ end
94
+
95
+ ##
96
+ # Write whole graph
97
+ #
98
+ # @param [Graph] graph
99
+ # @return [void]
100
+ def write_graph(graph)
101
+ @graph = graph
102
+ end
103
+
104
+ ##
105
+ # Addes a statement to be serialized
106
+ # @param [RDF::Statement] statement
107
+ # @return [void]
108
+ def write_statement(statement)
109
+ @graph.insert(statement)
110
+ end
111
+
112
+ ##
113
+ # Addes a triple to be serialized
114
+ # @param [RDF::Resource] subject
115
+ # @param [RDF::URI] predicate
116
+ # @param [RDF::Value] object
117
+ # @return [void]
118
+ # @abstract
119
+ def write_triple(subject, predicate, object)
120
+ @graph.insert(Statement.new(subject, predicate, object))
121
+ end
122
+
123
+ ##
124
+ # Outputs the RDF/XML representation of all stored triples.
125
+ #
126
+ # @return [void]
127
+ # @raise [RDF::WriterError] when attempting to write non-conformant graph
128
+ # @see #write_triple
129
+ def write_epilogue
130
+ @force_RDF_about = {}
131
+ @max_depth = @options[:max_depth] || 3
132
+ @base_uri = @options[:base_uri]
133
+ @lang = @options[:lang]
134
+ @attributes = @options[:attributes] || :none
135
+ @debug = @options[:debug]
136
+ raise RDF::WriterError, "Invalid attribute option '#{@attributes}', should be one of #{VALID_ATTRIBUTES.to_sentence}" unless VALID_ATTRIBUTES.include?(@attributes.to_sym)
137
+ self.reset
138
+
139
+ doc = Nokogiri::XML::Document.new
140
+
141
+ add_debug "\nserialize: graph of size #{@graph.size}"
142
+ add_debug "options: #{@options.inspect}"
143
+
144
+ preprocess
145
+
146
+ prefix(:rdf, RDF.to_uri)
147
+ prefix(:xml, RDF::XML) if @base_uri || @lang
148
+
149
+ add_debug "\nserialize: graph namespaces: #{prefixes.inspect}"
150
+
151
+ doc.root = Nokogiri::XML::Element.new("rdf:RDF", doc)
152
+ doc.root["xml:lang"] = @lang if @lang
153
+ doc.root["xml:base"] = @base_uri if @base_uri
154
+
155
+ # Add statements for each subject
156
+ order_subjects.each do |subject|
157
+ #add_debug "subj: #{subject.inspect}"
158
+ subject(subject, doc.root)
159
+ end
160
+
161
+ prefixes.each_pair do |p, uri|
162
+ if p == nil
163
+ doc.root.default_namespace = uri.to_s
164
+ else
165
+ doc.root.add_namespace(p.to_s, uri.to_s)
166
+ end
167
+ end
168
+
169
+ add_debug "doc:\n #{doc.to_xml(:encoding => "UTF-8", :indent => 2)}"
170
+ doc.write_xml_to(@output, :encoding => "UTF-8", :indent => 2)
171
+ end
172
+
173
+ # Return a QName for the URI, or nil. Adds namespace of QName to defined prefixes
174
+ # @param [URI,#to_s] resource
175
+ # @param [Hash<Symbol => Object>] options
176
+ # @option [Boolean] :with_default (false) If a default mapping exists, use it, otherwise if a prefixed mapping exists, use it
177
+ # @return [String, nil] value to use to identify URI
178
+ def get_qname(resource, options = {})
179
+ case resource
180
+ when RDF::Node
181
+ add_debug "qname(#{resource.inspect}): #{resource}"
182
+ return resource.to_s
183
+ when RDF::URI
184
+ uri = resource.to_s
185
+ else
186
+ add_debug "qname(#{resource.inspect}): nil"
187
+ return nil
188
+ end
189
+
190
+ qname = case
191
+ when options[:with_default] && prefix(nil) && uri.index(prefix(nil)) == 0
192
+ # Don't cache
193
+ add_debug "qname(#{resource.inspect}): #{uri.sub(prefix(nil), '').inspect} (default)"
194
+ return uri.sub(prefix(nil), '')
195
+ when @uri_to_qname.has_key?(uri)
196
+ add_debug "qname(#{resource.inspect}): #{@uri_to_qname[uri].inspect} (cached)"
197
+ return @uri_to_qname[uri]
198
+ when u = @uri_to_prefix.keys.detect {|u| uri.index(u.to_s) == 0 && NC_REGEXP.match(uri[u.to_s.length..-1])}
199
+ # Use a defined prefix
200
+ prefix = @uri_to_prefix[u]
201
+ prefix(prefix, u) # Define for output
202
+ uri.sub(u.to_s, "#{prefix}:")
203
+ when @options[:standard_prefixes] && vocab = RDF::Vocabulary.detect {|v| uri.index(v.to_uri.to_s) == 0 && NC_REGEXP.match(uri[v.to_uri.to_s.length..-1])}
204
+ prefix = vocab.__name__.to_s.split('::').last.downcase
205
+ @uri_to_prefix[vocab.to_uri.to_s] = prefix
206
+ prefix(prefix, vocab.to_uri) # Define for output
207
+ uri.sub(vocab.to_uri.to_s, "#{prefix}:")
208
+ else
209
+
210
+ # No vocabulary found, invent one
211
+ # Add bindings for predicates not already having bindings
212
+ # From RDF/XML Syntax and Processing:
213
+ # An XML namespace-qualified name (QName) has restrictions on the legal characters such that not all
214
+ # property URIs can be expressed as these names. It is recommended that implementors of RDF serializers,
215
+ # in order to break a URI into a namespace name and a local name, split it after the last XML non-NCName
216
+ # character, ensuring that the first character of the name is a Letter or '_'. If the URI ends in a
217
+ # non-NCName character then throw a "this graph cannot be serialized in RDF/XML" exception or error.
218
+ separation = uri.rindex(%r{[^a-zA-Z_0-9-][a-zA-Z_][a-z0-9A-Z_-]*$})
219
+ return @uri_to_qname[uri] = nil unless separation
220
+ base_uri = uri.to_s[0..separation]
221
+ suffix = uri.to_s[separation+1..-1]
222
+ @gen_prefix = @gen_prefix ? @gen_prefix.succ : "ns0"
223
+ @uri_to_prefix[base_uri] = @gen_prefix
224
+ prefix(@gen_prefix, base_uri)
225
+ "#{@gen_prefix}:#{suffix}"
226
+ end
227
+
228
+ add_debug "qname(#{resource.inspect}): #{qname.inspect}"
229
+ @uri_to_qname[uri] = qname
230
+ rescue Addressable::URI::InvalidURIError => e
231
+ raise RDF::WriterError, "Invalid URI #{uri.inspect}: #{e.message}"
232
+ end
233
+
234
+ protected
235
+ # If @base_uri is defined, use it to try to make uri relative
236
+ # @param [#to_s] uri
237
+ # @return [String]
238
+ def relativize(uri)
239
+ uri = uri.to_s
240
+ @base_uri ? uri.sub(@base_uri.to_s, "") : uri
241
+ end
242
+
243
+ # Defines rdf:type of subjects to be emitted at the beginning of the graph. Defaults to none
244
+ # @return [Array<URI>]
245
+ def top_classes; []; end
246
+
247
+ # Defines order of predicates to to emit at begninning of a resource description. Defaults to
248
+ # [rdf:type, rdfs:label, dc:title]
249
+ # @return [Array<URI>]
250
+ def predicate_order; [RDF.type, RDF::RDFS.label, RDF::DC.title]; end
251
+
252
+ # Order subjects for output. Override this to output subjects in another order.
253
+ #
254
+ # Uses top_classes
255
+ # @return [Array<Resource>] Ordered list of subjects
256
+ def order_subjects
257
+ seen = {}
258
+ subjects = []
259
+
260
+ top_classes.each do |class_uri|
261
+ graph.query(:predicate => RDF.type, :object => class_uri).map {|st| st.subject}.sort.uniq.each do |subject|
262
+ #add_debug "order_subjects: #{subject.inspect}"
263
+ subjects << subject
264
+ seen[subject] = @top_levels[subject] = true
265
+ end
266
+ end
267
+
268
+ # Sort subjects by resources over bnodes, ref_counts and the subject URI itself
269
+ recursable = @subjects.keys.
270
+ select {|s| !seen.include?(s)}.
271
+ map {|r| [(r.is_a?(RDF::Node) ? 1 : 0) + ref_count(r), r]}.
272
+ sort_by {|l| l.first }
273
+
274
+ subjects += recursable.map{|r| r.last}
275
+ end
276
+
277
+ # Perform any preprocessing of statements required
278
+ def preprocess
279
+ default_namespace = @options[:default_namespace] || prefix(nil)
280
+
281
+ # Load defined prefixes
282
+ (@options[:prefixes] || {}).each_pair do |k, v|
283
+ @uri_to_prefix[v.to_s] = k
284
+ end
285
+ @options[:prefixes] = {} # Will define actual used when matched
286
+
287
+ if default_namespace
288
+ add_debug("preprocess: default_namespace: #{default_namespace}")
289
+ prefix(nil, default_namespace)
290
+ end
291
+
292
+ @graph.each {|statement| preprocess_statement(statement)}
293
+ end
294
+
295
+ # Perform any statement preprocessing required. This is used to perform reference counts and determine required
296
+ # prefixes.
297
+ # @param [Statement] statement
298
+ def preprocess_statement(statement)
299
+ #add_debug "preprocess: #{statement.inspect}"
300
+ references = ref_count(statement.object) + 1
301
+ @references[statement.object] = references
302
+ @subjects[statement.subject] = true
303
+ end
304
+
305
+ # Returns indent string multiplied by the depth
306
+ # @param [Integer] modifier Increase depth by specified amount
307
+ # @return [String] A number of spaces, depending on current depth
308
+ def indent(modifier = 0)
309
+ " " * (@depth + modifier)
310
+ end
311
+
312
+ def reset
313
+ @depth = 0
314
+ @lists = {}
315
+ prefixes = {}
316
+ @references = {}
317
+ @serialized = {}
318
+ @subjects = {}
319
+ @top_levels = {}
320
+ end
321
+
322
+ private
323
+ def subject(subject, parent_node)
324
+ node = nil
325
+
326
+ raise RDF::WriterError, "Illegal use of subject #{subject.inspect}, not supported in RDF/XML" unless subject.resource?
327
+
328
+ if !is_done?(subject)
329
+ subject_done(subject)
330
+ properties = @graph.properties(subject)
331
+ add_debug "subject: #{subject.inspect}, props: #{properties.inspect}"
332
+
333
+ @graph.query(:subject => subject).each do |st|
334
+ raise RDF::WriterError, "Illegal use of predicate #{st.predicate.inspect}, not supported in RDF/XML" unless st.predicate.uri?
335
+ end
336
+
337
+ rdf_type, *rest = properties.fetch(RDF.type.to_s, [])
338
+ qname = get_qname(rdf_type, :with_default => true)
339
+ if rdf_type.is_a?(RDF::Node)
340
+ # Must serialize with an element
341
+ qname = rdf_type = nil
342
+ elsif rest.empty?
343
+ properties.delete(RDF.type.to_s)
344
+ else
345
+ properties[RDF.type.to_s] = [rest].flatten.compact
346
+ end
347
+ prop_list = order_properties(properties)
348
+ add_debug "=> property order: #{prop_list.to_sentence}"
349
+
350
+ if qname
351
+ rdf_type = nil
352
+ else
353
+ qname = "rdf:Description"
354
+ prefixes[:rdf] = RDF.to_uri
355
+ end
356
+
357
+ node = Nokogiri::XML::Element.new(qname, parent_node.document)
358
+
359
+ node["rdf:type"] = rdf_type if rdf_type
360
+
361
+ if subject.is_a?(RDF::Node)
362
+ # Only need nodeID if it's referenced elsewhere
363
+ if ref_count(subject) > (@depth == 0 ? 0 : 1)
364
+ node["rdf:nodeID"] = subject.id
365
+ else
366
+ node.add_child(Nokogiri::XML::Comment.new(node.document, "Serialization for #{subject}")) if RDF::RDFXML::debug?
367
+ end
368
+ else
369
+ node["rdf:about"] = relativize(subject)
370
+ end
371
+
372
+ prop_list.each do |prop|
373
+ prop_ref = RDF::URI.intern(prop)
374
+
375
+ properties[prop].each do |object|
376
+ raise RDF::WriterError, "Illegal use of object #{object.inspect}, not supported in RDF/XML" unless object.resource? || object.literal?
377
+
378
+ @depth += 1
379
+ predicate(prop_ref, object, node, properties[prop].length == 1)
380
+ @depth -= 1
381
+ end
382
+ end
383
+ elsif @force_RDF_about.include?(subject)
384
+ add_debug "subject: #{subject.inspect}, force about"
385
+ node = Nokogiri::XML::Element.new("rdf:Description", parent_node.document)
386
+ if subject.is_a?(RDF::Node)
387
+ node["rdf:nodeID"] = subject.id
388
+ else
389
+ node["rdf:about"] = relativize(subject)
390
+ end
391
+ end
392
+ @force_RDF_about.delete(subject)
393
+
394
+ parent_node.add_child(node) if node
395
+ end
396
+
397
+ # Output a predicate into the specified node.
398
+ #
399
+ # If _is_unique_ is true, this predicate may be able to be serialized as an attribute
400
+ def predicate(prop, object, node, is_unique)
401
+ as_attr = predicate_as_attribute?(prop, object) && is_unique
402
+
403
+ qname = get_qname(prop, :with_default => !as_attr)
404
+ raise RDF::WriterError, "No qname generated for <#{prop}>" unless qname
405
+
406
+ add_debug "predicate: #{qname}, as_attr: #{as_attr}, object: #{object.inspect}, done: #{is_done?(object)}, subject: #{@subjects.include?(object)}"
407
+ #qname = "rdf:li" if qname.match(/rdf:_\d+/)
408
+ pred_node = Nokogiri::XML::Element.new(qname, node.document)
409
+
410
+ o_props = @graph.properties(object)
411
+
412
+ col = RDF::List.new(object, @graph).to_a
413
+ conformant_list = col.all? {|item| !item.literal?} && o_props[RDF.first.to_s]
414
+ args = xml_args(object)
415
+ attrs = args.pop
416
+
417
+ # Check to see if it can be serialized as a collection
418
+ if conformant_list
419
+ add_debug("=> as collection: [#{col.map(&:to_s).join(", ")}]")
420
+ # Serialize list as parseType="Collection"
421
+ pred_node.add_child(Nokogiri::XML::Comment.new(node.document, "Serialization for #{object}")) if RDF::RDFXML::debug?
422
+ pred_node["rdf:parseType"] = "Collection"
423
+ while o_props[RDF.first.to_s]
424
+ # Object is used only for referencing collection item and next
425
+ subject_done(object)
426
+ item = o_props[RDF.first.to_s].first
427
+ object = o_props[RDF.rest.to_s].first
428
+ o_props = @graph.properties(object)
429
+ add_debug("=> li first: #{item}, rest: #{object}")
430
+ @force_RDF_about[item] = true
431
+ subject(item, pred_node)
432
+ end
433
+ elsif as_attr
434
+ # Serialize as attribute
435
+ pred_node.unlink
436
+ pred_node = nil
437
+ node[qname] = object.is_a?(RDF::URI) ? relativize(object) : object.value
438
+ add_debug("=> as attribute: node[#{qname}]=#{node[qname]}, #{object.class}")
439
+ elsif object.literal?
440
+ # Serialize as element
441
+ add_debug("predicate as element: #{attrs.inspect}")
442
+ attrs.each_pair do |a, av|
443
+ next if a.to_s == "xml:lang" && av.to_s == @lang # Lang already specified, don't repeat
444
+ add_debug "=> elt attr #{a}=#{av}"
445
+ pred_node[a] = av.to_s
446
+ end
447
+ add_debug "=> elt #{'xmllit ' if object.literal? && object.datatype == RDF.XMLLiteral}content=#{args.first}" if !args.empty?
448
+ if object.datatype == RDF.XMLLiteral
449
+ pred_node.inner_html = args.first.to_s
450
+ elsif args.first
451
+ pred_node.content = args.first
452
+ end
453
+ elsif @depth < @max_depth && !is_done?(object) && @subjects.include?(object)
454
+ add_debug(" as element (recurse)")
455
+ @depth += 1
456
+ subject(object, pred_node)
457
+ @depth -= 1
458
+ elsif object.is_a?(RDF::Node)
459
+ add_debug("=> as element (nodeID)")
460
+ pred_node["rdf:nodeID"] = object.id
461
+ else
462
+ add_debug("=> as element (resource)")
463
+ pred_node["rdf:resource"] = relativize(object)
464
+ end
465
+
466
+ node.add_child(pred_node) if pred_node
467
+ end
468
+
469
+ # Mark a subject as done.
470
+ def subject_done(subject)
471
+ add_debug("subject_done: #{subject}")
472
+ @serialized[subject] = true
473
+ end
474
+
475
+ # Return the number of times this node has been referenced in the object position
476
+ def ref_count(node)
477
+ @references.fetch(node, 0)
478
+ end
479
+
480
+ def is_done?(subject)
481
+ #add_debug("is_done?(#{subject}): #{@serialized.include?(subject)}")
482
+ @serialized.include?(subject)
483
+ end
484
+
485
+ # See if we can serialize as attribute.
486
+ # * untyped attributes that aren't duplicated where xml:lang == @lang
487
+ # * typed attributes that aren't duplicated if @dt_as_attr is true
488
+ # * rdf:type
489
+ def predicate_as_attribute?(prop, object)
490
+ [:untyped, :typed].include?(@attributes) && (
491
+ prop == RDF.type ||
492
+ [:typed].include?(@attributes) && object.literal? && object.typed? ||
493
+ (object.literal? && object.plain? || @lang && object.language.to_s == @lang.to_s)
494
+ )
495
+ end
496
+
497
+ # Take a hash from predicate uris to lists of values.
498
+ # Sort the lists of values. Return a sorted list of properties.
499
+ # @param [Hash{String => Array<Resource>}] properties A hash of Property to Resource mappings
500
+ # @return [Array<String>}] Ordered list of properties. Uses predicate_order.
501
+ def order_properties(properties)
502
+ properties.keys.each do |k|
503
+ properties[k] = properties[k].sort do |a, b|
504
+ a_li = a.is_a?(RDF::URI) && get_qname(a) && get_qname(a).to_s =~ /:_\d+$/ ? a.to_i : a.to_s
505
+ b_li = b.is_a?(RDF::URI) && get_qname(b) && get_qname(b).to_s =~ /:_\d+$/ ? b.to_i : b.to_s
506
+
507
+ a_li <=> b_li
508
+ end
509
+ end
510
+
511
+ # Make sorted list of properties
512
+ prop_list = []
513
+
514
+ predicate_order.each do |prop|
515
+ next unless properties[prop]
516
+ prop_list << prop.to_s
517
+ end
518
+
519
+ properties.keys.sort.each do |prop|
520
+ next if prop_list.include?(prop.to_s)
521
+ prop_list << prop.to_s
522
+ end
523
+
524
+ prop_list
525
+ end
526
+
527
+ # XML content and arguments for serialization
528
+ # Encoding.the_null_encoding.xml_args("foo", "en-US") => ["foo", {"xml:lang" => "en-US"}]
529
+ def xml_args(object)
530
+ case object
531
+ when RDF::Literal
532
+ if object.plain?
533
+ [object.value, {}]
534
+ elsif object.has_language?
535
+ [object.value, {"xml:lang" => object.language}]
536
+ elsif object.datatype == RDF.XMLLiteral
537
+ [object.value, {"rdf:parseType" => "Literal"}]
538
+ else
539
+ [object.value, {"rdf:datatype" => object.datatype.to_s}]
540
+ end
541
+ when RDF::Node
542
+ [{"rdf:nodeID" => object.id}]
543
+ when RDF::URI
544
+ [{"rdf:resource" => object.to_s}]
545
+ else
546
+ raise RDF::WriterError, "Attempt to serialize #{object.inspect}, not supported in RDF/XML"
547
+ end
548
+ end
549
+
550
+ # Add debug event to debug array, if specified
551
+ #
552
+ # @param [String] message::
553
+ def add_debug(message)
554
+ msg = "#{indent}#{message}"
555
+ STDERR.puts msg if ::RDF::RDFXML.debug?
556
+ @debug << msg if @debug.is_a?(Array)
557
+ end
558
+ end
559
+ end