Class: RDF::RDFXML::Writer
- Includes:
- Util::Logger
- Defined in:
- vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb
Overview
An RDF/XML serialiser in Ruby
Note that the natural interface is to write a whole graph at a time. Writing statements or Triples will create a graph to add them to and then serialize the graph.
The writer will add prefix definitions, and use them for creating @prefix definitions, and minting QNames
Constant Summary collapse
- VALID_ATTRIBUTES =
[:none, :untyped, :typed]
Constants included from Util::Logger
Instance Attribute Summary collapse
-
#base_uri ⇒ RDF::URI
Base URI used for relativizing URIs.
-
#graph ⇒ Graph
Graph of statements serialized.
-
#has_direction ⇒ Boolean
Set to true if any literal includes a base direction.
-
#top_classes ⇒ Array<URI>
readonly
Defines rdf:type of subjects to be emitted at the beginning of the document.
-
#version ⇒ String
RDF Version to output, if any.
Attributes inherited from Writer
Class Method Summary collapse
-
.options ⇒ Object
RDF/XML Writer options.
Instance Method Summary collapse
-
#initialize(output = $stdout, **options) {|writer| ... } ⇒ Writer
constructor
Initializes the RDF/XML writer instance.
-
#prefix_attrs ⇒ Hash{String => String}
protected
XML namespace attributes for defined prefixes.
-
#preprocess ⇒ ignored
protected
Perform any preprocessing of statements required.
-
#preprocess_statement(statement) ⇒ Object
protected
Perform any statement preprocessing required.
-
#render_collection(property, list, builder, **options, &block) ⇒ Object
protected
Render a collection, which may be included in a property declaration, or may be recursive within another collection.
-
#render_document(subjects, lang: nil, base: nil, **options) {|subject| ... } ⇒ Object
protected
Render document.
-
#render_property(subject, property, objects, builder, **options) ⇒ Object
protected
Render a single- or multi-valued property.
-
#render_subject(subject, builder, **options) {|predicate| ... } ⇒ Object
protected
Render a subject.
-
#render_triple_term(term, builder, **options) ⇒ Object
protected
Render a triple term, which may be recursive.
-
#reset ⇒ Object
protected
Reset parser to run again.
- #write_epilogue ⇒ Object
-
#write_triple(subject, predicate, object)
abstract
Addes a triple to be serialized.
Methods included from Util::Logger
#log_debug, #log_depth, #log_error, #log_fatal, #log_info, #log_recover, #log_recovering?, #log_statistics, #log_warn, #logger
Methods inherited from Writer
accept?, buffer, #canonicalize?, dump, each, #encoding, #escaped, #flush, for, format, #format_list, #format_literal, #format_node, #format_term, #format_tripleTerm, #format_uri, #node_id, open, #prefix, #prefixes, #prefixes=, #puts, #quoted, #to_sym, to_sym, #uri_for, #validate?, #write_comment, #write_prologue, #write_statement, #write_triples
Methods included from Util::Aliasing::LateBound
Methods included from Writable
#<<, #insert, #insert_graph, #insert_reader, #insert_statement, #insert_statements, #writable?
Methods included from Util::Coercions
Constructor Details
#initialize(output = $stdout, **options) {|writer| ... } ⇒ Writer
Initializes the RDF/XML writer instance.
134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 134 def initialize(output = $stdout, **, &block) super do @graph = RDF::Graph.new @uri_to_prefix = {} @uri_to_qname = {} @top_classes = [:top_classes] || [RDF::RDFS.Class] # FIXME: If version is specified in media type, use it to set an explicit version @version = nil block.call(self) if block_given? end end |
Instance Attribute Details
#base_uri ⇒ RDF::URI
Returns Base URI used for relativizing URIs.
64 65 66 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 64 def base_uri @base_uri end |
#graph ⇒ Graph
Returns Graph of statements serialized.
61 62 63 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 61 def graph @graph end |
#has_direction ⇒ Boolean
Returns Set to true if any literal includes a base direction.
70 71 72 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 70 def has_direction @has_direction end |
#top_classes ⇒ Array<URI> (readonly)
Defines rdf:type of subjects to be emitted at the beginning of the document.
58 59 60 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 58 def top_classes @top_classes end |
#version ⇒ String
Returns RDF Version to output, if any.
67 68 69 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 67 def version @version end |
Class Method Details
.options ⇒ Object
RDF/XML Writer options
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 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 75 def self. super + [ RDF::CLI::Option.new( symbol: :attributes, datatype: %w(none untyped typed), on: ["--attributes ATTRIBUTES", %w(none untyped typed)], description: "How to use XML attributes when serializing, one of :none, :untyped, :typed. The default is :none.") {|arg| arg.to_sym}, RDF::CLI::Option.new( symbol: :default_namespace, datatype: RDF::URI, on: ["--default-namespace URI", :REQUIRED], description: "URI to use as default namespace, same as prefixes.") {|arg| RDF::URI(arg)}, RDF::CLI::Option.new( symbol: :lang, datatype: String, on: ["--lang LANG", :REQUIRED], description: "Output as root xml:lang attribute, and avoid generation xml:lang, where possible.") {|arg| RDF::URI(arg)}, RDF::CLI::Option.new( symbol: :max_depth, datatype: Integer, on: ["--max-depth"], description: "Maximum depth for recursively defining resources, defaults to 3.") {|arg| arg.to_i}, RDF::CLI::Option.new( symbol: :stylesheet, datatype: RDF::URI, on: ["--stylesheet URI", :REQUIRED], description: "URI to use as @href for output stylesheet processing instruction.") {|arg| RDF::URI(arg)}, ] end |
Instance Method Details
#prefix_attrs ⇒ Hash{String => String} (protected)
XML namespace attributes for defined prefixes
443 444 445 446 447 448 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 443 def prefix_attrs prefixes.inject({}) do |memo, (k, v)| memo[(k ? "xmlns:#{k}" : "xmlns").to_sym] = v.to_s memo end end |
#preprocess ⇒ ignored (protected)
Perform any preprocessing of statements required
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 452 def preprocess # Load defined prefixes (@options[:prefixes] || {}).each_pair do |k, v| @uri_to_prefix[v.to_s] = k end @options[:prefixes] = {} # Will define actual used when matched prefix(:rdf, RDF.to_uri) @uri_to_prefix[RDF.to_uri.to_s] = :rdf if base_uri || @options[:lang] prefix(:xml, RDF::XML) @uri_to_prefix[RDF::XML.to_s] = :xml end if @options[:default_namespace] @uri_to_prefix[@options[:default_namespace]] = nil prefix(nil, @options[:default_namespace]) end # Process each statement to establish QNames and Terms @graph.each {|statement| preprocess_statement(statement)} end |
#preprocess_statement(statement) ⇒ Object (protected)
Perform any statement preprocessing required. This is used to perform reference counts and determine required prefixes.
For RDF/XML, make sure that all predicates have QNames
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 479 def preprocess_statement(statement) #log_debug {"preprocess: #{statement.inspect}"} bump_reference(statement.object) @subjects[statement.subject] = true get_qname(statement.subject) ensure_qname(statement.predicate) statement.predicate == RDF.type && statement.object.uri? ? ensure_qname(statement.object) : get_qname(statement.object) get_qname(statement.object.datatype) if statement.object.literal? && statement.object.datatype? # Base direction requires a prefix, used to set the its:version in the document if statement.object.literal? && statement.object.direction? prefix(:its, RDF::ITS.to_s) @has_direction = true # Indirectly adds its:version to document element # It's an error if version is frozen and not at least "1.2-basic" if version && version.frozen? log_error("Literal direction is incompatible with required version #{version}: #{statement.object.direction}") if version == "1.1" elsif version.nil? @version = "1.2-basic" end elsif statement.object.statement? # It's an error if version is frozen and not at least "1.2-basic" if version && version.frozen? log_error("Triple terms are incompatible with required version #{version}") if version != "1.2" else @version = "1.2" end bump_reference(statement.subject) # If this statement is also asserted, note it as an annotation # Also count references of triple terms preprocess_statement(statement.object) if statement.object.statement? # If it fits, allow this to be rendered as an annotation if statement.predicate == RDF.reifies @reification[statement.subject] ||= [] @reification[statement.subject] << statement.object unless @reification[statement.subject].include?(statement.object) end end end |
#render_collection(property, list, builder, **options, &block) ⇒ Object (protected)
Render a collection, which may be included in a property declaration, or may be recursive within another collection
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 423 def render_collection(property, list, builder, **, &block) builder.tag!(property, "rdf:parseType": "Collection") do |b| list.each do |object| if log_depth <= @max_depth && !is_done?(object) render_subject(object, b) elsif object.node? if ref_count(object) > 1 b.tag!("rdf:Description", "rdf:nodeID": object.id) else b.tag!("rdf:Description") end else b.tag!("rdf:Description", "rdf:about": object.relativize(base_uri)) end end end end |
#render_document(subjects, lang: nil, base: nil, **options) {|subject| ... } ⇒ Object (protected)
Render document. Yields each subject to be rendered separately.
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 225 def render_document(subjects, lang: nil, base: nil, **, &block) builder = Builder::RdfXml.new(indent: 2) builder.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8" builder.instruct! :'xml-stylesheet', type: 'text/xsl', href: [:stylesheet] if [:stylesheet] attrs = prefix_attrs attrs[:"xml:lang"] = lang if lang attrs[:"xml:base"] = base if base attrs[:"rdf:version"] = version.freeze if version attrs[:"its:version"] = "2.0" if has_direction builder.rdf(:RDF, **attrs) do |b| subjects.each do |subject| render_subject(subject, b, **) end end end |
#render_property(subject, property, objects, builder, **options) ⇒ Object (protected)
Render a single- or multi-valued property. Yields each object for optional rendering. The block should only render for recursive subject definitions (i.e., where the object is also a subject and is rendered underneath the first referencing subject).
If a multi-valued property definition is not found within the template, the writer will use the single-valued property definition multiple times.
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 318 def render_property(subject, property, objects, builder, **) log_debug {"render_property(#{property}): #{objects.inspect}"} property = get_qname(property) if property.is_a?(RDF::URI) # Separate out the objects which are lists and render separately lists = objects. select(&:node?). map {|o| RDF::List.new(subject: o, graph: @graph)}. select {|l| l.valid? && l.none?(&:literal?)} objects = objects - lists.map(&:subject) unless lists.empty? # Render non-list objects log_debug(depth: log_depth + 1) {"properties with lists: #{lists} non-lists: #{objects - lists.map(&:subject)}"} unless objects.empty? render_property(subject, property, objects, builder, **) end # Render each list lists.each do |list| # Render each list as multiple properties and set :inlist to true list.each_statement {|st| subject_done(st.subject)} log_depth do log_debug {"list: #{list.inspect} #{list.to_a}"} render_collection(property, list, builder, **) end end end if objects.length == 1 recurse = log_depth <= @max_depth object = objects.first attrs = {} # If there is a single reifier for this statement, write out annotation tt = RDF::Statement(subject, @uri_to_qname.invert[property], object) reifs = @reification.select {|k, v| v.include?(tt)}.keys if reifs.length == 1 reif = reifs.first @as_annotation[reif] = tt if reif.iri? attrs['rdf:annotation'] = reif.relativize(base_uri) else attrs['rdf:annotationNodeID'] = reif.id end end if recurse && !is_done?(object) builder.tag!(property, **attrs) do |b| render_subject(object, b, **) end elsif object.literal? && object.datatype == RDF.XMLLiteral builder.tag!(property, "rdf:parseType": "Literal", no_whitespace: true, **attrs) do |b| b << object.value end elsif object.literal? attrs[:"xml:lang"] = object.language if object.language? attrs[:"rdf:datatype"] = object.datatype if object.datatype? attrs[:"its:dir"] = object.direction if object.direction? builder.tag!(property, object.value.to_s, **attrs) elsif object.statement? # Just write out the triple term, unless it is annotated builder.tag!(property, "rdf:parseType": "Triple") do |b| render_triple_term(object, b, **) end unless @as_annotation.key?(subject) elsif object.node? builder.tag!(property, "rdf:nodeID": object.id, **attrs) elsif object builder.tag!(property, "rdf:resource": object.relativize(base_uri), **attrs) end else # Render each property using property_value template objects.each do |object| log_depth do render_property(subject, property, [object], builder, **) end end end end |
#render_subject(subject, builder, **options) {|predicate| ... } ⇒ Object (protected)
Render a subject.
The subject template may be called either as a top-level element, or recursively under another element if the rel local is not nil.
For RDF/XML, removes from predicates those that can be rendered as attributes, and adds the :attr_props
local, which includes all attributes to be rendered as properties.
Yields each property to be rendered separately.
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 272 def render_subject(subject, builder, **, &block) return nil if is_done?(subject) attr_props, , types = prop_partition(properties_for_subject(subject)) # The first type is used for first_type = types.shift type_qname = get_qname(first_type) if first_type && !first_type.node? type_qname = nil unless type_qname.is_a?(String) types.unshift(first_type) if first_type && !type_qname type_qname ||= "rdf:Description" attr_props = attr_props.merge("rdf:nodeID": subject.id) if subject.node? && ref_count(subject) >= 1 attr_props = attr_props.merge("rdf:about": subject.relativize(base_uri)) if subject.uri? log_debug {"render_subject(#{subject.inspect})"} subject_done(subject) builder.tag!(type_qname, **attr_props) do |b| types.each do |type| if type.node? b.tag!("rdf:type", "rdf:nodeID": type.id) else b.tag!("rdf:type", "rdf:resource": type.to_s) end end log_depth do .each do |p, objects| render_property(subject, p, objects, b, **) end end end end |
#render_triple_term(term, builder, **options) ⇒ Object (protected)
Render a triple term, which may be recursive
403 404 405 406 407 408 409 410 411 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 403 def render_triple_term(term, builder, **) attr_props = {} attr_props = attr_props.merge("rdf:nodeID": term.subject.id) if term.subject.node? attr_props = attr_props.merge("rdf:about": term.subject.relativize(base_uri)) if term.subject.uri? builder.tag!("rdf:Description", **attr_props) do |b| render_property(term.subject, term.predicate, [term.object], b) end end |
#reset ⇒ Object (protected)
Reset parser to run again
194 195 196 197 198 199 200 201 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 194 def reset @options[:log_depth] = 0 @references = {} @serialized = {} @subjects = {} @reification = {} @as_annotation = {} end |
#write_epilogue ⇒ Object
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 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 161 def write_epilogue @max_depth = @options.fetch(:max_depth, 10) @attributes = @options.fetch(:attributes, :none) @base_uri = RDF::URI(@options[:base_uri]) if @options[:base_uri] @lang = @options[:lang] self.reset log_debug {"\nserialize: graph size: #{@graph.size}"} preprocess # Prefixes prefix = prefixes.keys.map {|pk| "#{pk}: #{prefixes[pk]}"}.sort.join(" ") unless prefixes.empty? log_debug {"\nserialize: prefixes: #{prefix.inspect}"} @subjects = order_subjects # Generate document doc = render_document(@subjects, lang: @lang, base: base_uri, prefix: prefix, stylesheet: @options[:stylesheet]) do |s| subject(s) end @output.write(doc) super end |
#write_triple(subject, predicate, object)
This method returns an undefined value.
Addes a triple to be serialized
157 158 159 |
# File 'vendor/bundler/ruby/3.4.0/bundler/gems/rdf-rdfxml-14ee5432437b/lib/rdf/rdfxml/writer.rb', line 157 def write_triple(subject, predicate, object) @graph.insert(RDF::Statement(subject, predicate, object)) end |