Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

More Blank Node renaming side-effects.

  • Loading branch information...
commit af415a4cf26502c92990de7a986154c457dee9f8 1 parent efc267a
@gkellogg authored
View
24 Gemfile.lock
@@ -1,6 +1,6 @@
GIT
remote: git://github.com/ruby-rdf/rdf.git
- revision: 8671f547f28579fe6acdf44e6463fdec1e9180f5
+ revision: 9ebede6b36b856b5be4e41d9f0a4d175428207aa
specs:
rdf (0.3.11)
addressable (>= 2.2.6)
@@ -19,21 +19,21 @@ GEM
addressable (2.3.2)
backports (2.6.5)
columnize (0.3.6)
- debugger (1.2.2)
+ debugger (1.2.3)
columnize (>= 0.3.1)
debugger-linecache (~> 1.1.1)
debugger-ruby_core_source (~> 1.1.5)
debugger-linecache (1.1.2)
debugger-ruby_core_source (>= 1.1.1)
- debugger-ruby_core_source (1.1.5)
+ debugger-ruby_core_source (1.1.6)
diff-lcs (1.1.3)
equivalent-xml (0.3.0)
nokogiri (>= 1.4.3)
haml (3.1.7)
htmlentities (4.3.1)
- json (1.7.5)
- json (1.7.5-java)
- json_pure (1.7.5)
+ json (1.7.6)
+ json (1.7.6-java)
+ json_pure (1.7.6)
linkeddata (0.3.5)
json-ld (>= 0.1.0)
rdf (>= 0.3.5)
@@ -46,8 +46,8 @@ GEM
rdf-trig (>= 0.1.1)
rdf-trix (>= 0.3.0)
rdf-turtle (>= 0.1.1)
- nokogiri (1.5.5)
- nokogiri (1.5.5-java)
+ nokogiri (1.5.6)
+ nokogiri (1.5.6-java)
open-uri-cached (0.0.5)
promise (0.3.0)
rdf-isomorphic (0.3.4)
@@ -62,7 +62,7 @@ GEM
rdf-xsd (>= 0.3.4)
rdf-n3 (0.3.8)
rdf (>= 0.3.11)
- rdf-rdfa (0.3.18)
+ rdf-rdfa (0.3.19.1)
haml (>= 3.1.7)
htmlentities (>= 4.3.1)
rdf (>= 0.3.11)
@@ -85,10 +85,10 @@ GEM
rspec-core (~> 2.12.0)
rspec-expectations (~> 2.12.0)
rspec-mocks (~> 2.12.0)
- rspec-core (2.12.1)
- rspec-expectations (2.12.0)
+ rspec-core (2.12.2)
+ rspec-expectations (2.12.1)
diff-lcs (~> 1.1.3)
- rspec-mocks (2.12.0)
+ rspec-mocks (2.12.1)
spira (0.0.12)
promise (>= 0.3.0)
rdf (>= 0.2.3)
View
43 lib/json/ld/api.rb
@@ -28,9 +28,22 @@ class API
OPEN_OPTS = {
:headers => %w(Accept: application/ld+json, application/json)
}
+
+ # Current input
+ # @!attribute [rw] input
+ # @return [String, #read, Hash, Array]
attr_accessor :value
+
+ # Input evaluation context
+ # @!attribute [rw] context
+ # @return [JSON::LD::EvaluationContext]
attr_accessor :context
+ # Current Blank Node Namer
+ # @!attribute [r] namer
+ # @return [JSON::LD::BlankNodeNamer]
+ attr_reader :namer
+
##
# Initialize the API, reading in any document and setting global options
#
@@ -59,6 +72,8 @@ class API
# @yieldparam [API]
def initialize(input, context, options = {}, &block)
@options = {:compactArrays => true}.merge(options)
+ options = {:rename_bnodes => true}.merge(options)
+ @namer = options[:rename_bnodes] ? BlankNodeNamer.new("t") : BlankNodeMapper.new
@value = case input
when Array, Hash then input.dup
when IO, StringIO then JSON.parse(input.read)
@@ -103,11 +118,9 @@ def initialize(input, context, options = {}, &block)
# The expanded JSON-LD document
# @see http://json-ld.org/spec/latest/json-ld-api/#expansion-algorithm
def self.expand(input, context = nil, callback = nil, options = {})
- options = {:rename_bnodes => true}.merge(options)
result = nil
- namer = options[:rename_bnodes] ? BlankNodeNamer.new("t") : BlankNodeMapper.new
API.new(input, context, options) do |api|
- result = api.expand(api.value, nil, api.context, namer)
+ result = api.expand(api.value, nil, api.context)
end
# If, after the algorithm outlined above is run, the resulting element is an
@@ -212,11 +225,7 @@ def self.flatten(input, graph, context, callback = nil, options = {})
# Generate _nodeMap_
node_map = Hash.ordered
- self.generate_node_map(value,
- node_map,
- (graph.to_s == '@merged' ? '@merged' : '@default'),
- nil,
- BlankNodeNamer.new("t"))
+ self.generate_node_map(value, node_map, (graph.to_s == '@merged' ? '@merged' : '@default'))
result = []
@@ -309,11 +318,7 @@ def self.frame(input, frame, callback = nil, options = {})
# Get framing nodes from expanded input, replacing Blank Node identifiers as necessary
all_nodes = Hash.ordered
depth do
- generate_node_map(value,
- all_nodes,
- '@merged',
- nil,
- BlankNodeNamer.new("t"))
+ generate_node_map(value, all_nodes, '@merged')
end
@node_map = all_nodes['@merged']
debug(".frame") {"node_map: #{@node_map.to_json(JSON_STATE)}"}
@@ -358,14 +363,14 @@ def self.frame(input, frame, callback = nil, options = {})
# @yield statement
# @yieldparam [RDF::Statement] statement
def self.toRDF(input, context = nil, callback = nil, options = {})
- # 1) Perform the Expansion Algorithm on the JSON-LD input.
- # This removes any existing context to allow the given context to be cleanly applied.
- expanded = expand(input, context, nil, options)
+ API.new(input, context, options) do |api|
+ # 1) Perform the Expansion Algorithm on the JSON-LD input.
+ # This removes any existing context to allow the given context to be cleanly applied.
+ result = api.expand(api.value, nil, api.context)
- API.new(expanded, nil, options) do
- debug(".expand") {"expanded input: #{value.to_json(JSON_STATE)}"}
+ api.send(:debug, ".expand") {"expanded input: #{result.to_json(JSON_STATE)}"}
# Start generating statements
- statements("", value, nil, nil, nil) do |statement|
+ api.statements("", result, nil, nil, nil) do |statement|
callback.call(statement) if callback
yield statement if block_given?
end
View
10 lib/json/ld/compact.rb
@@ -55,12 +55,18 @@ def compact(element, property = nil)
# FIXME: check for full-iri list coercion
# Otherwise store the resulting array as value of active property if empty or property otherwise.
- compacted_key = context.compact_iri(k, :position => :predicate, :depth => @depth)
+ compacted_key = context.compact_iri('@list', :position => :predicate, :depth => @depth)
v = depth { compact(element[k], property) }
# Return either the result as an array, as an object with a key of @list (or appropriate alias from active context
v = [v].compact unless v.is_a?(Array)
- v = {compacted_key => v} unless context.container(property) == k
+ unless context.container(property) == '@list'
+ v = {compacted_key => v}
+ if element['@annotation']
+ compacted_key = context.compact_iri('@annotation', :position => :predicate, :depth => @depth)
+ v[compacted_key] = element['@annotation']
+ end
+ end
debug("compact") {"@list result, return as #{v.inspect}"}
return v
end
View
22 lib/json/ld/evaluation_context.rb
@@ -520,8 +520,6 @@ def term_valid?(term)
# @see http://json-ld.org/spec/latest/json-ld-api/#iri-expansion
def expand_iri(iri, options = {})
return iri unless iri.is_a?(String)
- raise "attempt to expand #{iri} having cyclic definition" if (options[:path] ||= []).include?(iri.to_s)
- options[:path] << iri.to_s
prefix, suffix = iri.split(':', 2)
unless (m = mapping(iri)) == false
@@ -591,7 +589,7 @@ def compact_iri(iri, options = {})
# If value is a @list select terms that match every item equivalently.
debug("compact_iri", "#{value.inspect} is a list? #{list?(value).inspect}") if value
- if list?(value)
+ if list?(value) && !annotation?(value)
list_terms = matched_terms.select {|t| container(t) == '@list'}
terms = list_terms.inject({}) do |memo, t|
@@ -935,13 +933,7 @@ def term_rank(term, value)
container(term) == '@list' ? 1 : 0
else
# Otherwise, return the most specific term, for which the term has some match against every value.
- depth do
- value['@list'].map do |v|
- r = term_rank(term, v)
- raise "rank = 0" if r == 0
- r
- end.max rescue 0
- end
+ depth {value['@list'].map {|v| term_rank(term, v)}}.min
end
elsif value?(value)
val_type = value.fetch('@type', nil)
@@ -953,7 +945,15 @@ def term_rank(term, value)
default_term ? 2 : 1
elsif val_lang.nil?
debug("val_lang.nil") {"#{language(term).inspect} && #{coerce(term).inspect}"}
- language(term) == false || (default_term && default_language.nil?) ? 3 : 0
+ if language(term) == false || (default_term && default_language.nil?)
+ # Value has no language, and there is no default language and the term has no language
+ 3
+ elsif default_term
+ # The term has no language (or type), but it's different than the default
+ 2
+ else
+ 0
+ end
else
if val_lang && container(term) == '@language'
3
View
22 lib/json/ld/expand.rb
@@ -10,10 +10,9 @@ module Expand
# @param [Array, Hash] input
# @param [String] active_property
# @param [EvaluationContext] context
- # @param [BlankNodeNamer] namer
# @param [Hash{Symbol => Object}] options
# @return [Array, Hash]
- def expand(input, active_property, context, namer, options = {})
+ def expand(input, active_property, context, options = {})
debug("expand") {"input: #{input.inspect}, active_property: #{active_property.inspect}, context: #{context.inspect}"}
result = case input
when Array
@@ -27,7 +26,7 @@ def expand(input, active_property, context, namer, options = {})
# throw an exception as lists of lists are not allowed.
raise ProcessingError::ListOfLists, "A list may not contain another list" if v.is_a?(Array) && is_list
- expand(v, active_property, context, namer, options)
+ expand(v, active_property, context, options)
end.flatten.compact
if is_list && value.any? {|v| v.is_a?(Hash) && v.has_key?('@list')}
@@ -109,7 +108,7 @@ def expand(input, active_property, context, namer, options = {})
# using this algorithm, passing copies of the active context and active property.
# If the expanded value is not an array, convert it to an array.
value = [value] unless value.is_a?(Array)
- value = depth { expand(value, active_property, context, namer, options) }
+ value = depth { expand(value, active_property, context, options) }
# If property is @list, and any expanded value
# is an object containing an @list property, throw an exception, as lists of lists are not supported
@@ -154,7 +153,7 @@ def expand(input, active_property, context, namer, options = {})
value.keys.sort.each do |k|
[value[k]].flatten.each do |v|
# Expand the value, adding an '@annotation' key with value equal to the key
- expanded_value = depth { expand(v, active_property, context, namer, options) }
+ expanded_value = depth { expand(v, active_property, context, options) }
next unless expanded_value
expanded_value['@annotation'] ||= k
ary << expanded_value
@@ -164,7 +163,7 @@ def expand(input, active_property, context, namer, options = {})
ary
else
# Otherwise, expand value recursively using this algorithm, passing copies of the active context and active property.
- depth { expand(value, active_property, context, namer, options) }
+ depth { expand(value, active_property, context, options) }
end
end
@@ -199,7 +198,7 @@ def expand(input, active_property, context, namer, options = {})
end
if property.is_a?(Array)
- label_blanknodes(expanded_value, namer)
+ label_blanknodes(expanded_value)
property.map(&:to_s).each do |prop|
# label all blank nodes in value with blank node identifiers by using the Label Blank Nodes Algorithm.
output_object[prop] ||= []
@@ -269,15 +268,14 @@ def expand(input, active_property, context, namer, options = {})
protected
# @param [Array, Hash] input
- # @param [BlankNodeNamer] namer
- def label_blanknodes(element, namer)
+ def label_blanknodes(element)
if element.is_a?(Array)
- element.each {|e| label_blanknodes(e, namer)}
+ element.each {|e| label_blanknodes(e)}
elsif list?(element)
- element['@list'].each {|e| label_blanknodes(e, namer)}
+ element['@list'].each {|e| label_blanknodes(e)}
elsif element.is_a?(Hash)
element.keys.sort.each do |k|
- label_blanknodes(element[k], namer)
+ label_blanknodes(element[k])
end
if node?(element) and !element.has_key?('@id')
element['@id'] = namer.get_name(nil)
View
14 lib/json/ld/flatten.rb
@@ -13,14 +13,13 @@ module Flatten
# Graph name for results
# @param [Array] list
# List for saving list elements
- # @param [BlankNodeNamer] namer
- # @param [String] id
+ # @param [String] id (nil)
# Identifier already associated with element
- def generate_node_map(element, node_map, graph, list, namer, id = nil)
+ def generate_node_map(element, node_map, graph, list = nil, id = nil)
depth do
debug("nodeMap") {"element: #{element.inspect}, graph: #{graph}"}
if element.is_a?(Array)
- element.map {|o| generate_node_map(o, node_map, graph, list, namer)}
+ element.map {|o| generate_node_map(o, node_map, graph, list)}
elsif !element.is_a?(Hash) || value?(element)
list << element if list
else
@@ -48,7 +47,7 @@ def generate_node_map(element, node_map, graph, list, namer, id = nil)
when '@graph'
# If property is @graph, recursively call this algorithm passing value for element, nodeMap, nil for list and if graph is @merged use graph, otherwise use id for graph and then continue.
graph = graph == '@merged' ? '@merged' : id
- generate_node_map(value, node_map, graph, nil, namer)
+ generate_node_map(value, node_map, graph)
when /^@(?!type)/
# If property is not @type and is a keyword, merge property and value into node and then continue.
debug("nodeMap") {"merge keyword#{prop}: #{value.inspect}"}
@@ -75,7 +74,7 @@ def generate_node_map(element, node_map, graph, list, namer, id = nil)
}
# Recursively call this algorithm passing v for value, nodeMap, graph, and nil for list.
- generate_node_map(v, node_map, graph, nil, namer, name)
+ generate_node_map(v, node_map, graph, nil, name)
elsif list?(v)
# Otherwise if v has the property @list then recursively call this algorithm with the value of @list as element, nodeMap, graph, and a new array flattenedList as list.
debug("nodeMap") {"list value #{prop}: #{v.inspect}"}
@@ -83,8 +82,7 @@ def generate_node_map(element, node_map, graph, list, namer, id = nil)
generate_node_map(v['@list'],
node_map,
graph,
- flattened_list,
- namer)
+ flattened_list)
# Create a new JSON object with the property @list set to flattenedList and add it to node for property.
(node[prop] ||= []) << {'@list' => flattened_list}
elsif prop == '@type'
View
6 lib/json/ld/to_rdf.rb
@@ -1,3 +1,4 @@
+require 'rdf'
require 'rdf/nquads'
module JSON::LD
@@ -21,7 +22,6 @@ module ToRDF
# @yieldparam [RDF::Statement] statement
def statements(path, element, subject, property, name, &block)
debug(path) {"statements: e=#{element.inspect}, s=#{subject.inspect}, p=#{property.inspect}, n=#{name.inspect}"}
- @node_seq = "t0" unless subject || property
traverse_result = depth do
case element
@@ -154,9 +154,7 @@ def parse_list(path, list, property, name, &block)
##
# Create a new named node using the sequence
def node
- n = RDF::Node.new(@node_seq)
- @node_seq = @node_seq.succ
- n
+ RDF::Node.new(namer.get_sym)
end
##
View
27 lib/json/ld/utils.rb
@@ -36,7 +36,16 @@ def blank_node?(value)
# @param [Object] value
# @return [Boolean]
def list?(value)
- value.is_a?(Hash) && value.keys == %w(@list)
+ value.is_a?(Hash) && value.has_key?('@list')
+ end
+
+ ##
+ # Is value annotated?
+ #
+ # @param [Object] value
+ # @return [Boolean]
+ def annotation?(value)
+ value.is_a?(Hash) && value.has_key?('@annotation')
end
##
@@ -78,19 +87,21 @@ def depth(options = {})
# nodes to new identifiers
class BlankNodeMapper < Hash
##
- # Just return a Blank Node based on `old`
- # @param [String] old
+ # Just return a Blank Node based on `old`. Manufactures
+ # a node if `old` is nil or empty
+ # @param [String] old ("")
# @return [String]
- def get_sym(old)
+ def get_sym(old = "")
+ old = RDF::Node.new.to_s if old.to_s.empty?
old.to_s.sub(/_:/, '')
end
##
# Get a new mapped name for `old`
#
- # @param [String] old
+ # @param [String] old ("")
# @return [String]
- def get_name(old)
+ def get_name(old = "")
"_:" + get_sym(old)
end
end
@@ -105,9 +116,9 @@ def initialize(prefix)
##
# Get a new symbol mapped from `old`
- # @param [String] old
+ # @param [String] old ("")
# @return [String]
- def get_sym(old)
+ def get_sym(old = "")
old = old.to_s.sub(/_:/, '')
if old && self.has_key?(old)
self[old]
View
123 spec/evaluation_context_spec.rb
@@ -780,6 +780,7 @@ def ctx.content_type; "application/ld+json"; end
{
"listplain" => [
[{"@value" => "foo"}],
+ [{"@value" => "foo"}, {"@value" => "bar"}, {"@value" => "baz"}],
[{"@value" => "foo"}, {"@value" => "bar"}, {"@value" => 1}],
[{"@value" => "foo"}, {"@value" => "bar"}, {"@value" => 1.1}],
[{"@value" => "foo"}, {"@value" => "bar"}, {"@value" => true}],
@@ -803,6 +804,112 @@ def ctx.content_type; "application/ld+json"; end
end
end
+ context "compact-0018" do
+ let(:ctx) do
+ subject.parse(JSON.parse %({
+ "id1": "http://example.com/id1",
+ "type1": "http://example.com/t1",
+ "type2": "http://example.com/t2",
+ "@language": "de",
+ "term": {
+ "@id": "http://example.com/term"
+ },
+ "term1": {
+ "@id": "http://example.com/term",
+ "@container": "@list"
+ },
+ "term2": {
+ "@id": "http://example.com/term",
+ "@container": "@list",
+ "@language": "en"
+ },
+ "term3": {
+ "@id": "http://example.com/term",
+ "@container": "@list",
+ "@language": null
+ },
+ "term4": {
+ "@id": "http://example.com/term",
+ "@container": "@list",
+ "@type": "type1"
+ },
+ "term5": {
+ "@id": "http://example.com/term",
+ "@container": "@list",
+ "@type": "type2"
+ }
+ }))
+ end
+
+ {
+ "term" => [
+ '{ "@value": "v0.1", "@language": "de" }',
+ '{ "@value": "v0.2", "@language": "en" }',
+ '{ "@value": "v0.3"}',
+ '{ "@value": 4}',
+ '{ "@value": true}',
+ '{ "@value": false}'
+ ],
+ "term1" => %q({
+ "@list": [
+ { "@value": "v1.1", "@language": "de" },
+ { "@value": "v1.2", "@language": "en" },
+ { "@value": "v1.3"},
+ { "@value": 14},
+ { "@value": true},
+ { "@value": false}
+ ]
+ }),
+ "term2" => %q({
+ "@list": [
+ { "@value": "v2.1", "@language": "en" },
+ { "@value": "v2.2", "@language": "en" },
+ { "@value": "v2.3", "@language": "en" },
+ { "@value": "v2.4", "@language": "en" },
+ { "@value": "v2.5", "@language": "en" },
+ { "@value": "v2.6", "@language": "en" }
+ ]
+ }),
+ "term3" => %q({
+ "@list": [
+ { "@value": "v3.1"},
+ { "@value": "v3.2"},
+ { "@value": "v3.3"},
+ { "@value": "v3.4"},
+ { "@value": "v3.5"},
+ { "@value": "v3.6"}
+ ]
+ }),
+ "term4" => %q({
+ "@list": [
+ { "@value": "v4.1", "@type": "http://example.com/t1" },
+ { "@value": "v4.2", "@type": "http://example.com/t1" },
+ { "@value": "v4.3", "@type": "http://example.com/t1" },
+ { "@value": "v4.4", "@type": "http://example.com/t1" },
+ { "@value": "v4.5", "@type": "http://example.com/t1" },
+ { "@value": "v4.6", "@type": "http://example.com/t1" }
+ ]
+ }),
+ "term5" => %q({
+ "@list": [
+ { "@value": "v5.1", "@type": "http://example.com/t2" },
+ { "@value": "v5.2", "@type": "http://example.com/t2" },
+ { "@value": "v5.3", "@type": "http://example.com/t2" },
+ { "@value": "v5.4", "@type": "http://example.com/t2" },
+ { "@value": "v5.5", "@type": "http://example.com/t2" },
+ { "@value": "v5.6", "@type": "http://example.com/t2" }
+ ]
+ }),
+ }.each do |term, value|
+ [value].flatten.each do |v|
+ it "Uses #{term} for #{v}" do
+ ctx.compact_iri("http://example.com/term", :value => JSON.parse(v)).
+ should produce(term, @debug)
+ end
+ end
+ end
+ end
+
context "compact-0020" do
let(:ctx) do
subject.parse({
@@ -828,6 +935,18 @@ def ctx.content_type; "application/ld+json"; end
should produce("sub:id/1", @debug)
end
end
+
+ context "compact-0041" do
+ let(:ctx) do
+ subject.parse({"name" => {"@id" => "http://example.com/property", "@container" => "@list"}})
+ end
+ it "Does not use @list with @annotation" do
+ ctx.compact_iri("http://example.com/property", :value => {
+ "@list" => ["one item"],
+ "@annotation" => "an annotation"
+ }).should produce("http://example.com/property", @debug)
+ end
+ end
end
describe "#term_rank" do
@@ -938,11 +1057,11 @@ def ctx.content_type; "application/ld+json"; end
"boolean value" => {:value => {"@value" => true}, :rank => 2},
"integer value" => {:value => {"@value" => 1}, :rank => 2},
"double value" => {:value => {"@value" => 1.1}, :rank => 2},
- "string value" => {:value => {"@value" => "foo"}, :rank => 0},
+ "string value" => {:value => {"@value" => "foo"}, :rank => 2},
"date" => {:value => {"@value" => "2012-04-17", "@type" => RDF::XSD.date.to_s}, :rank => 1},
"lang" => {:value => {"@value" => "apple", "@language" => "en"}, :rank => 2},
"id" => {:value => {"@id" => "http://example/id"}, :rank => 1},
- "value string" => {:value => {"@value" => "foo"}, :rank => 0},
+ "value string" => {:value => {"@value" => "foo"}, :rank => 2},
"null" => {:value => nil, :rank => 3},
},
"boolean" => {
View
8 spec/flatten_spec.rb
@@ -99,13 +99,11 @@
@node_map = Hash.ordered
graph = params[:graph] || '@merged'
jld = nil
- JSON::LD::API.new(params[:input], nil, :debug => @debug) do |api|
- expanded_value = api.expand(api.value, nil, api.context, JSON::LD::BlankNodeNamer.new("t"))
+ expanded_value = JSON::LD::API.expand(params[:input])
+ JSON::LD::API.new(expanded_value, nil, :debug => @debug) do |api|
api.generate_node_map(expanded_value,
@node_map,
- graph,
- nil,
- JSON::LD::BlankNodeNamer.new("t"))
+ graph)
end
@node_map.keys.should produce([graph], @debug)
subjects = @node_map[graph]
View
4 spec/suite_compact_spec.rb
@@ -11,8 +11,8 @@
specify "#{t.property('input')}: #{t.name}" do
begin
case t.property('input')
- when /compact-(0018|0024|0027)/
- pending("term rank")
+ #when /compact-(0018|0024|0027)/
+ # pending("term rank")
when /compact-(0031|0032|0033|0034|0035|0036|0037)/
pending("implementation of property generators")
end
View
2  spec/to_rdf_spec.rb
@@ -100,7 +100,7 @@
%q({
"@type": "_:foo"
}),
- %q([ a _:a ] .)
+ %q([ a _:foo ] .)
]
}.each do |title, (js, nt)|
it title do
View
2  spec/writer_spec.rb
@@ -154,7 +154,7 @@
owl:onClass <http://data.wikia.com/terms#Element>;
owl:onProperty <http://data.wikia.com/terms#characterIn> .
)
- serialize(input, :prefixes => {
+ serialize(input, :rename_bnodes => false, :prefixes => {
:owl => "http://www.w3.org/2002/07/owl#",
:rdfs => "http://www.w3.org/2000/01/rdf-schema#",
:xsd => "http://www.w3.org/2001/XMLSchema#"
Please sign in to comment.
Something went wrong with that request. Please try again.