Skip to content

Commit b3e943d

Browse files
committed
Have toRdf emit only valid triples, excluding anything invalid. Literal validity is based on the value being a string, language having the proper form, and datatype IRI being valid.
1 parent 3eba4ef commit b3e943d

File tree

3 files changed

+106
-14
lines changed

3 files changed

+106
-14
lines changed

lib/json/ld/api.rb

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -455,19 +455,9 @@ def self.toRdf(input, expanded: false, **options, &block)
455455
item_to_rdf(node) do |statement|
456456
next if statement.predicate.node? && !options[:produceGeneralizedRdf]
457457

458-
# Drop results with relative IRIs
459-
relative = statement.to_a.any? do |r|
460-
case r
461-
when RDF::URI
462-
r.relative?
463-
when RDF::Literal
464-
r.has_datatype? && r.datatype.relative?
465-
else
466-
false
467-
end
468-
end
469-
if relative
470-
log_debug(".toRdf") {"drop statement with relative IRIs: #{statement.to_ntriples}"}
458+
# Drop invalid statements (other than IRIs)
459+
unless statement.valid_extended?
460+
log_debug(".toRdf") {"drop invalid statement: #{statement.to_nquads}"}
471461
next
472462
end
473463

lib/json/ld/extensions.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,39 @@ def +(value)
77
Node.new(id + value.to_s)
88
end
99
end
10+
11+
class Statement
12+
# Validate extended RDF
13+
def valid_extended?
14+
has_subject? && subject.resource? && subject.valid_extended? &&
15+
has_predicate? && predicate.resource? && predicate.valid_extended? &&
16+
has_object? && object.term? && object.valid_extended? &&
17+
(has_graph? ? (graph_name.resource? && graph_name.valid_extended?) : true)
18+
end
19+
end
20+
21+
class URI
22+
# Validate extended RDF
23+
def valid_extended?
24+
self.valid?
25+
end
26+
end
27+
28+
class Node
29+
# Validate extended RDF
30+
def valid_extended?
31+
self.valid?
32+
end
33+
end
34+
35+
class Literal
36+
# Validate extended RDF
37+
def valid_extended?
38+
return false if language? && language.to_s !~ /^[a-zA-Z]+(-[a-zA-Z0-9]+)*$/
39+
return false if datatype? && datatype.invalid?
40+
value.is_a?(String)
41+
end
42+
end
1043
end
1144

1245
class Array

spec/to_rdf_spec.rb

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,14 +667,70 @@
667667
%q(
668668
[<http://rdfs.org/sioc/ns#content> "foo"^^<http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral>] .
669669
)
670-
]
670+
],
671671
}.each do |title, (js, ttl)|
672672
it title do
673673
ttl = "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . #{ttl}"
674674
expect(parse(js)).to be_equivalent_graph(ttl, logger: logger, inputDocument: js)
675675
end
676676
end
677677
end
678+
679+
context "exceptions" do
680+
{
681+
"Invalid subject" => {
682+
input: %({
683+
"@id": "http://example.com/a b",
684+
"http://example.com/foo": "bar"
685+
}),
686+
output: %()
687+
},
688+
"Invalid predicate" => {
689+
input: %({
690+
"@id": "http://example.com/foo",
691+
"http://example.com/a b": "bar"
692+
}),
693+
output: %()
694+
},
695+
"Invalid object" => {
696+
input: %({
697+
"@id": "http://example.com/foo",
698+
"http://example.com/bar": {"@id": "http://example.com/baz z"}
699+
}),
700+
output: %()
701+
},
702+
"Invalid type" => {
703+
input: %({
704+
"@id": "http://example.com/foo",
705+
"@type": ["http://example.com/bar", "relative"]
706+
}),
707+
output: %(<http://example.com/foo> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.com/bar> .)
708+
},
709+
"Invalid language" => {
710+
input: %({
711+
"@id": "http://example.com/foo",
712+
"http://example.com/bar": {"@value": "bar", "@language": "a b"}
713+
}),
714+
output: %()
715+
},
716+
"Invalid datatype" => {
717+
input: %({
718+
"@id": "http://example.com/foo",
719+
"http://example.com/bar": {"@value": "bar", "@type": "http://example.com/baz z"}
720+
}),
721+
output: %()
722+
},
723+
"Injected IRIs check" => {
724+
input: %({
725+
"@id": "http://foo/> <http://bar/> <http://baz> .\n<data:little> <data:bobby> <data:tables> .\n<data:in-ur-base",
726+
"http://killin/#yer": "dudes"
727+
}),
728+
output: %()
729+
},
730+
}.each do |title, params|
731+
it(title) {run_to_rdf params}
732+
end
733+
end
678734
end
679735

680736
def parse(input, options = {})
@@ -683,4 +739,17 @@ def parse(input, options = {})
683739
JSON::LD::API.toRdf(StringIO.new(input), options) {|st| graph << st}
684740
graph
685741
end
742+
743+
def run_to_rdf(params)
744+
input, output, processingMode = params[:input], params[:output], params[:processingMode]
745+
graph = params[:graph] || RDF::Graph.new
746+
input = StringIO.new(input) if input.is_a?(String)
747+
pending params.fetch(:pending, "test implementation") unless input
748+
if params[:exception]
749+
expect {JSON::LD::API.toRdf(input, {processingMode: processingMode}.merge(params))}.to raise_error(params[:exception])
750+
else
751+
JSON::LD::API.toRdf(input, base: params[:base], logger: logger, processingMode: processingMode) {|st| graph << st}
752+
expect(graph).to be_equivalent_graph(output, logger: logger, inputDocument: input)
753+
end
754+
end
686755
end

0 commit comments

Comments
 (0)