json-ld 1.0.1.1 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +14 -6
- data/README.md +2 -2
- data/VERSION +1 -1
- data/lib/json/ld/api.rb +8 -5
- data/lib/json/ld/compact.rb +2 -1
- data/lib/json/ld/context.rb +44 -33
- data/lib/json/ld/expand.rb +1 -1
- data/lib/json/ld/flatten.rb +9 -11
- data/lib/json/ld/from_rdf.rb +48 -45
- data/lib/json/ld/reader.rb +5 -0
- data/lib/json/ld/to_rdf.rb +5 -23
- data/lib/json/ld/utils.rb +9 -2
- data/spec/api_spec.rb +1 -1
- data/spec/compact_spec.rb +1 -2
- data/spec/flatten_spec.rb +3 -20
- data/spec/from_rdf_spec.rb +16 -29
- data/spec/matchers.rb +3 -3
- data/spec/reader_spec.rb +4 -4
- data/spec/suite_compact_spec.rb +0 -4
- data/spec/suite_to_rdf_spec.rb +2 -2
- data/spec/test-files/test-1-rdf.ttl +3 -3
- data/spec/test-files/test-3-rdf.ttl +1 -1
- data/spec/test-files/test-5-rdf.ttl +2 -2
- data/spec/test-files/test-6-rdf.ttl +2 -2
- data/spec/test-files/test-7-rdf.ttl +6 -6
- data/spec/to_rdf_spec.rb +32 -18
- data/spec/writer_spec.rb +0 -2
- metadata +33 -33
data/lib/json/ld/reader.rb
CHANGED
@@ -50,6 +50,11 @@ module JSON::LD
|
|
50
50
|
# @see RDF::Reader#each_statement
|
51
51
|
def each_statement(&block)
|
52
52
|
JSON::LD::API.toRDF(@doc, @options[:context], @options).each do |statement|
|
53
|
+
# If RDF version is 1.0, fold literals with xsd:string to be just simple literals
|
54
|
+
statement.object.datatype = nil if
|
55
|
+
RDF::VERSION.to_s < "1.1" &&
|
56
|
+
statement.object.literal? &&
|
57
|
+
statement.object.datatype == RDF::XSD.string
|
53
58
|
block.call(statement)
|
54
59
|
end
|
55
60
|
end
|
data/lib/json/ld/to_rdf.rb
CHANGED
@@ -20,7 +20,7 @@ module JSON::LD
|
|
20
20
|
# For each id-node in active_graph
|
21
21
|
active_graph.each do |id, node|
|
22
22
|
# Initialize subject as the IRI or BNode representation of id
|
23
|
-
subject = as_resource(id)
|
23
|
+
subject = as_resource(id, context.doc_base)
|
24
24
|
debug("graph_to_rdf") {"subject: #{subject.to_ntriples}"}
|
25
25
|
|
26
26
|
# For each property-values in node
|
@@ -29,7 +29,7 @@ module JSON::LD
|
|
29
29
|
when '@type'
|
30
30
|
# If property is @type, construct triple as an RDF Triple composed of id, rdf:type, and object from values where id and object are represented either as IRIs or Blank Nodes
|
31
31
|
results += values.map do |value|
|
32
|
-
object = as_resource(value)
|
32
|
+
object = as_resource(value, context.doc_base)
|
33
33
|
debug("graph_to_rdf") {"type: #{object.to_ntriples}"}
|
34
34
|
RDF::Statement.new(subject, RDF.type, object)
|
35
35
|
end
|
@@ -38,7 +38,7 @@ module JSON::LD
|
|
38
38
|
else
|
39
39
|
# Otherwise, property is an IRI or Blank Node identifier
|
40
40
|
# Initialize predicate from property as an IRI or Blank node
|
41
|
-
predicate = as_resource(property)
|
41
|
+
predicate = as_resource(property, context.doc_base)
|
42
42
|
debug("graph_to_rdf") {"predicate: #{predicate.to_ntriples}"}
|
43
43
|
|
44
44
|
# For each item in values
|
@@ -90,7 +90,7 @@ module JSON::LD
|
|
90
90
|
value = lit.to_s
|
91
91
|
datatype ||= lit.datatype
|
92
92
|
else
|
93
|
-
# Otherwise, if datatype is null, set it to xsd:string or
|
93
|
+
# Otherwise, if datatype is null, set it to xsd:string or xsd:langString, depending on if item has a @language key.
|
94
94
|
datatype ||= item.has_key?('@language') ? RDF.langString : RDF::XSD.string
|
95
95
|
end
|
96
96
|
|
@@ -104,7 +104,7 @@ module JSON::LD
|
|
104
104
|
# Otherwise, value must be a node definition containing only @id whos value is an IRI or Blank Node identifier
|
105
105
|
raise "Expected node reference, got #{item.inspect}" unless item.keys == %w(@id)
|
106
106
|
# Return value associated with @id as an IRI or Blank node
|
107
|
-
as_resource(item['@id'])
|
107
|
+
as_resource(item['@id'], context.doc_base)
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
@@ -145,23 +145,5 @@ module JSON::LD
|
|
145
145
|
def node
|
146
146
|
RDF::Node.new(namer.get_sym)
|
147
147
|
end
|
148
|
-
|
149
|
-
##
|
150
|
-
# add a statement, object can be literal or URI or bnode
|
151
|
-
#
|
152
|
-
# @param [String] path
|
153
|
-
# @param [RDF::Resource] subject the subject of the statement
|
154
|
-
# @param [RDF::URI] predicate the predicate of the statement
|
155
|
-
# @param [RDF::Term] object the object of the statement
|
156
|
-
# @param [RDF::Resource] name the named graph context of the statement
|
157
|
-
# @yield statement
|
158
|
-
# @yieldparam [RDF::Statement] statement
|
159
|
-
def add_quad(path, subject, predicate, object, name)
|
160
|
-
predicate = RDF.type if predicate == '@type'
|
161
|
-
object = context.expand_iri(object.to_s, :quiet => true) if object.literal? && predicate == RDF.type
|
162
|
-
statement = RDF::Statement.new(subject, predicate, object, :context => name)
|
163
|
-
debug(path) {"statement: #{statement.to_nquads}"}
|
164
|
-
yield statement
|
165
|
-
end
|
166
148
|
end
|
167
149
|
end
|
data/lib/json/ld/utils.rb
CHANGED
@@ -65,10 +65,17 @@ module JSON::LD
|
|
65
65
|
##
|
66
66
|
# Represent an id as an IRI or Blank Node
|
67
67
|
# @param [String] id
|
68
|
+
# @param [RDF::URI] base (nil)
|
68
69
|
# @return [RDF::Resource]
|
69
|
-
def as_resource(id)
|
70
|
+
def as_resource(id, base = nil)
|
70
71
|
@nodes ||= {} # Re-use BNodes
|
71
|
-
id[0,2] == '_:'
|
72
|
+
if id[0,2] == '_:'
|
73
|
+
(@nodes[id] ||= RDF::Node.new(id[2..-1]))
|
74
|
+
elsif base
|
75
|
+
base.join(id)
|
76
|
+
else
|
77
|
+
RDF::URI(id)
|
78
|
+
end
|
72
79
|
end
|
73
80
|
|
74
81
|
private
|
data/spec/api_spec.rb
CHANGED
@@ -33,7 +33,7 @@ describe JSON::LD::API do
|
|
33
33
|
end if File.exist?(framed) && File.exist?(frame)
|
34
34
|
|
35
35
|
it "toRDF" do
|
36
|
-
RDF::
|
36
|
+
RDF::Repository.load(filename, :debug => @debug).should be_equivalent_graph(RDF::Repository.load(ttl), :trace => @debug)
|
37
37
|
end if File.exist?(ttl)
|
38
38
|
end
|
39
39
|
end
|
data/spec/compact_spec.rb
CHANGED
@@ -336,8 +336,7 @@ describe JSON::LD::API do
|
|
336
336
|
"@context": {
|
337
337
|
"name": "http://xmlns.com/foaf/0.1/name",
|
338
338
|
"isKnownBy": {
|
339
|
-
"@reverse": "http://xmlns.com/foaf/0.1/knows"
|
340
|
-
"@type": "@id"
|
339
|
+
"@reverse": "http://xmlns.com/foaf/0.1/knows"
|
341
340
|
}
|
342
341
|
},
|
343
342
|
"@id": "http://example.com/people/markus",
|
data/spec/flatten_spec.rb
CHANGED
@@ -10,8 +10,7 @@ describe JSON::LD::API do
|
|
10
10
|
"single object" => {
|
11
11
|
:input => {"@id" => "http://example.com", "@type" => RDF::RDFS.Resource.to_s},
|
12
12
|
:output => [
|
13
|
-
{"@id" => "http://example.com", "@type" => [RDF::RDFS.Resource.to_s]}
|
14
|
-
{"@id" => RDF::RDFS.Resource.to_s}
|
13
|
+
{"@id" => "http://example.com", "@type" => [RDF::RDFS.Resource.to_s]}
|
15
14
|
]
|
16
15
|
},
|
17
16
|
"embedded object" => {
|
@@ -35,9 +34,7 @@ describe JSON::LD::API do
|
|
35
34
|
{
|
36
35
|
"@id" => "http://greggkellogg.net/foaf#me",
|
37
36
|
"@type" => [RDF::FOAF.Person.to_s]
|
38
|
-
}
|
39
|
-
{"@id" => RDF::FOAF.Person.to_s},
|
40
|
-
{"@id" => RDF::FOAF.PersonalProfile.to_s},
|
37
|
+
}
|
41
38
|
]
|
42
39
|
},
|
43
40
|
"embedded anon" => {
|
@@ -60,9 +57,7 @@ describe JSON::LD::API do
|
|
60
57
|
"@id" => "http://greggkellogg.net/foaf",
|
61
58
|
"@type" => [RDF::FOAF.PersonalProfile.to_s],
|
62
59
|
RDF::FOAF.primaryTopic.to_s => [{"@id" => "_:b0"}]
|
63
|
-
}
|
64
|
-
{"@id" => RDF::FOAF.Person.to_s},
|
65
|
-
{"@id" => RDF::FOAF.PersonalProfile.to_s},
|
60
|
+
}
|
66
61
|
]
|
67
62
|
},
|
68
63
|
"reverse properties" => {
|
@@ -149,8 +144,6 @@ describe JSON::LD::API do
|
|
149
144
|
{"@id": "http://www.brockhaus.de/"}
|
150
145
|
],
|
151
146
|
"@graph": [{
|
152
|
-
"@id": "http://example.org/location/France#this"
|
153
|
-
}, {
|
154
147
|
"@id": "http://example.org/location/Paris#this",
|
155
148
|
"http://example.org/locatedIn": [{"@id": "http://example.org/location/France#this"}]
|
156
149
|
}]
|
@@ -162,14 +155,6 @@ describe JSON::LD::API do
|
|
162
155
|
"@id": "http://example.org/location/Paris#this",
|
163
156
|
"http://example.org/hasPopulation": [{"@value": 7000000}]
|
164
157
|
}]
|
165
|
-
}, {
|
166
|
-
"@id": "http://www.britannica.com/"
|
167
|
-
}, {
|
168
|
-
"@id": "http://www.brockhaus.de/"
|
169
|
-
}, {
|
170
|
-
"@id": "http://www.w3.org/1999/02/22-rdf-syntax-ns#Graph"
|
171
|
-
}, {
|
172
|
-
"@id": "http://www.wikipedia.org/"
|
173
158
|
}])),
|
174
159
|
},
|
175
160
|
"Test Manifest (shortened)" => {
|
@@ -193,8 +178,6 @@ describe JSON::LD::API do
|
|
193
178
|
"@id": "#t0001",
|
194
179
|
"http://example/input": [{"@id": "error-expand-0001-in.jsonld"}],
|
195
180
|
"http://example/name": [{"@value": "Keywords cannot be aliased to other keywords"}]
|
196
|
-
}, {
|
197
|
-
"@id": "error-expand-0001-in.jsonld"
|
198
181
|
}]
|
199
182
|
}),
|
200
183
|
:options => {}
|
data/spec/from_rdf_spec.rb
CHANGED
@@ -12,9 +12,7 @@ describe JSON::LD::API do
|
|
12
12
|
{
|
13
13
|
'@id' => "http://a/b",
|
14
14
|
"http://a/c" => [{"@id" => "http://a/d"}]
|
15
|
-
|
16
|
-
'@id' => 'http://a/d'
|
17
|
-
}
|
15
|
+
}
|
18
16
|
], @debug)
|
19
17
|
end
|
20
18
|
|
@@ -27,9 +25,7 @@ describe JSON::LD::API do
|
|
27
25
|
{"@id" => "http://example.com/d"},
|
28
26
|
{"@id" => "http://example.com/e"}
|
29
27
|
]
|
30
|
-
}
|
31
|
-
{"@id" => "http://example.com/d"},
|
32
|
-
{"@id" => "http://example.com/e"}
|
28
|
+
}
|
33
29
|
], @debug)
|
34
30
|
end
|
35
31
|
|
@@ -40,9 +36,7 @@ describe JSON::LD::API do
|
|
40
36
|
'@id' => "http://example.com/b",
|
41
37
|
"http://example.com/c" => [{"@id" => "http://example.com/d"}],
|
42
38
|
"http://example.com/e" => [{"@id" => "http://example.com/f"}]
|
43
|
-
}
|
44
|
-
{"@id" => "http://example.com/d"},
|
45
|
-
{"@id" => "http://example.com/f"}
|
39
|
+
}
|
46
40
|
], @debug)
|
47
41
|
end
|
48
42
|
|
@@ -55,7 +49,6 @@ describe JSON::LD::API do
|
|
55
49
|
)
|
56
50
|
serialize(input).
|
57
51
|
should produce([
|
58
|
-
{'@id' => "http://www.w3.org/2006/03/test-description#TestCase"},
|
59
52
|
{'@id' => "test-cases/0001", '@type' => ["http://www.w3.org/2006/03/test-description#TestCase"]},
|
60
53
|
{'@id' => "test-cases/0002", '@type' => ["http://www.w3.org/2006/03/test-description#TestCase"]},
|
61
54
|
], @debug)
|
@@ -170,8 +163,7 @@ describe JSON::LD::API do
|
|
170
163
|
{
|
171
164
|
"@id" => "_:a",
|
172
165
|
"http://example.com/a" => [{"@id" => "http://example.com/b"}]
|
173
|
-
}
|
174
|
-
{"@id" => "http://example.com/b"}
|
166
|
+
}
|
175
167
|
], @debug)
|
176
168
|
end
|
177
169
|
|
@@ -185,8 +177,7 @@ describe JSON::LD::API do
|
|
185
177
|
{
|
186
178
|
"@id" => "http://example.com/a",
|
187
179
|
"http://example.com/b" => [{"@id" => "_:a"}]
|
188
|
-
}
|
189
|
-
{"@id" => "http://example.com/d"},
|
180
|
+
}
|
190
181
|
], @debug)
|
191
182
|
end
|
192
183
|
end
|
@@ -218,7 +209,7 @@ describe JSON::LD::API do
|
|
218
209
|
{"@id" => "http://example.com/c"}
|
219
210
|
]
|
220
211
|
}]
|
221
|
-
}
|
212
|
+
}], @debug)
|
222
213
|
end
|
223
214
|
|
224
215
|
it "should generate empty list" do
|
@@ -265,7 +256,7 @@ describe JSON::LD::API do
|
|
265
256
|
"@graph" => [{
|
266
257
|
"@id" => "http://example.com/a",
|
267
258
|
"http://example.com/b" => [{"@id" => "http://example.com/c"}]
|
268
|
-
}
|
259
|
+
}]
|
269
260
|
},
|
270
261
|
]
|
271
262
|
},
|
@@ -280,10 +271,9 @@ describe JSON::LD::API do
|
|
280
271
|
"@graph" => [{
|
281
272
|
"@id" => "http://example.com/a",
|
282
273
|
"http://example.com/b" => [{"@id" => "http://example.com/c"}]
|
283
|
-
}
|
274
|
+
}],
|
284
275
|
"http://example.com/d" => [{"@id" => "http://example.com/e"}]
|
285
|
-
}
|
286
|
-
{"@id" => "http://example.com/e"}
|
276
|
+
}
|
287
277
|
]
|
288
278
|
},
|
289
279
|
"with lists" => {
|
@@ -301,10 +291,9 @@ describe JSON::LD::API do
|
|
301
291
|
"@graph" => [{
|
302
292
|
"@id" => "http://example.com/a",
|
303
293
|
"http://example.com/b" => [{"@list" => [{"@id" => "http://example.com/c"}]}]
|
304
|
-
}
|
294
|
+
}],
|
305
295
|
"http://example.com/d" => [{"@list" => [{"@id" => "http://example.com/e"}]}]
|
306
|
-
}
|
307
|
-
{"@id" => "http://example.com/e"}
|
296
|
+
}
|
308
297
|
]
|
309
298
|
},
|
310
299
|
"Two Graphs with same subject and lists" => {
|
@@ -325,8 +314,7 @@ describe JSON::LD::API do
|
|
325
314
|
"http://example.com/b" => [{
|
326
315
|
"@list" => [{"@id" => "http://example.com/c"}]
|
327
316
|
}]
|
328
|
-
}
|
329
|
-
{"@id" => "http://example.com/c"}
|
317
|
+
}
|
330
318
|
]
|
331
319
|
},
|
332
320
|
{
|
@@ -337,8 +325,7 @@ describe JSON::LD::API do
|
|
337
325
|
"http://example.com/b" => [{
|
338
326
|
"@list" => [{"@id" => "http://example.com/e"}]
|
339
327
|
}]
|
340
|
-
}
|
341
|
-
{"@id" => "http://example.com/e"}
|
328
|
+
}
|
342
329
|
]
|
343
330
|
}
|
344
331
|
]
|
@@ -357,7 +344,7 @@ describe JSON::LD::API do
|
|
357
344
|
serialize(input, :useRdfType => false).should produce([{
|
358
345
|
'@id' => "http://example.com/a",
|
359
346
|
"@type" => ["http://example.com/b"]
|
360
|
-
}
|
347
|
+
}], @debug)
|
361
348
|
end
|
362
349
|
|
363
350
|
it "does not use @type if set to true" do
|
@@ -365,7 +352,7 @@ describe JSON::LD::API do
|
|
365
352
|
serialize(input, :useRdfType => true).should produce([{
|
366
353
|
'@id' => "http://example.com/a",
|
367
354
|
'@type' => ["http://example.com/b"]
|
368
|
-
}
|
355
|
+
}], @debug)
|
369
356
|
end
|
370
357
|
end
|
371
358
|
|
@@ -383,7 +370,7 @@ describe JSON::LD::API do
|
|
383
370
|
"http://www.w3.org/2000/01/rdf-schema#range" => [
|
384
371
|
{ "@id" => "http://www.w3.org/2001/XMLSchema#boolean" }
|
385
372
|
]
|
386
|
-
}
|
373
|
+
}]
|
387
374
|
],
|
388
375
|
}.each do |t, (input, output)|
|
389
376
|
it "#{t}" do
|
data/spec/matchers.rb
CHANGED
@@ -6,12 +6,12 @@ Info = Struct.new(:about, :information, :trace, :inputDocument, :outputDocument,
|
|
6
6
|
|
7
7
|
def normalize(graph)
|
8
8
|
case graph
|
9
|
-
when RDF::
|
9
|
+
when RDF::Enumerable then graph
|
10
10
|
when IO, StringIO
|
11
11
|
RDF::Graph.new.load(graph, :base => @info.about)
|
12
12
|
else
|
13
13
|
# Figure out which parser to use
|
14
|
-
g = RDF::
|
14
|
+
g = RDF::Repository.new
|
15
15
|
reader_class = detect_format(graph)
|
16
16
|
reader_class.new(graph, :base => @info.about).each {|s| g << s}
|
17
17
|
g
|
@@ -37,7 +37,7 @@ RSpec::Matchers.define :be_equivalent_graph do |expected, info|
|
|
37
37
|
|
38
38
|
failure_message_for_should do |actual|
|
39
39
|
info = @info.respond_to?(:information) ? @info.information : @info.inspect
|
40
|
-
if @expected.is_a?(RDF::
|
40
|
+
if @expected.is_a?(RDF::Enumerable) && @actual.size != @expected.size
|
41
41
|
"Graph entry count differs:\nexpected: #{@expected.size}\nactual: #{@actual.size}"
|
42
42
|
elsif @expected.is_a?(Array) && @actual.size != @expected.length
|
43
43
|
"Graph entry count differs:\nexpected: #{@expected.length}\nactual: #{@actual.size}"
|
data/spec/reader_spec.rb
CHANGED
@@ -36,7 +36,7 @@ describe JSON::LD::Reader do
|
|
36
36
|
|
37
37
|
describe "#initialize" do
|
38
38
|
it "yields reader given string" do
|
39
|
-
inner =
|
39
|
+
inner = double("inner")
|
40
40
|
inner.should_receive(:called).with(JSON::LD::Reader)
|
41
41
|
JSON::LD::Reader.new(subject) do |reader|
|
42
42
|
inner.called(reader.class)
|
@@ -44,7 +44,7 @@ describe JSON::LD::Reader do
|
|
44
44
|
end
|
45
45
|
|
46
46
|
it "yields reader given IO" do
|
47
|
-
inner =
|
47
|
+
inner = double("inner")
|
48
48
|
inner.should_receive(:called).with(JSON::LD::Reader)
|
49
49
|
JSON::LD::Reader.new(StringIO.new(subject)) do |reader|
|
50
50
|
inner.called(reader.class)
|
@@ -58,7 +58,7 @@ describe JSON::LD::Reader do
|
|
58
58
|
|
59
59
|
describe "#each_statement" do
|
60
60
|
it "yields statements" do
|
61
|
-
inner =
|
61
|
+
inner = double("inner")
|
62
62
|
inner.should_receive(:called).with(RDF::Statement).exactly(3)
|
63
63
|
JSON::LD::Reader.new(subject).each_statement do |statement|
|
64
64
|
inner.called(statement.class)
|
@@ -68,7 +68,7 @@ describe JSON::LD::Reader do
|
|
68
68
|
|
69
69
|
describe "#each_triple" do
|
70
70
|
it "yields statements" do
|
71
|
-
inner =
|
71
|
+
inner = double("inner")
|
72
72
|
inner.should_receive(:called).exactly(3)
|
73
73
|
JSON::LD::Reader.new(subject).each_triple do |subject, predicate, object|
|
74
74
|
inner.called(subject.class, predicate.class, object.class)
|
data/spec/suite_compact_spec.rb
CHANGED
@@ -10,10 +10,6 @@ describe JSON::LD do
|
|
10
10
|
m.entries.each do |t|
|
11
11
|
specify "#{t.property('input')}: #{t.name}" do
|
12
12
|
begin
|
13
|
-
#case t.property('input')
|
14
|
-
#when /compact-(0032|0033|0034)/
|
15
|
-
# pending("undesireable property generator corner cases")
|
16
|
-
#end
|
17
13
|
t.debug = ["test: #{t.inspect}", "source: #{t.input.read}"]
|
18
14
|
if t.property('context')
|
19
15
|
t.debug << "context: #{t.context.read}"
|
data/spec/suite_to_rdf_spec.rb
CHANGED
@@ -18,8 +18,8 @@ describe JSON::LD do
|
|
18
18
|
to_quad(statement)
|
19
19
|
end
|
20
20
|
|
21
|
-
sorted_expected = t.expect.readlines.sort.join("")
|
22
|
-
quads.sort.join("").should produce(sorted_expected, t.debug)
|
21
|
+
sorted_expected = t.expect.readlines.uniq.sort.join("")
|
22
|
+
quads.uniq.sort.join("").should produce(sorted_expected, t.debug)
|
23
23
|
rescue JSON::LD::ProcessingError => e
|
24
24
|
fail("Processing error: #{e.message}")
|
25
25
|
rescue JSON::LD::InvalidContext => e
|
@@ -3,6 +3,6 @@
|
|
3
3
|
@prefix name: <http://xmlns.com/foaf/0.1/name> .
|
4
4
|
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
5
5
|
|
6
|
-
[ avatar: "http://twitter.com/account/profile_image/manusporny"
|
7
|
-
homepage: "http://manu.sporny.org/"
|
8
|
-
name: "Manu Sporny"
|
6
|
+
[ avatar: "http://twitter.com/account/profile_image/manusporny";
|
7
|
+
homepage: "http://manu.sporny.org/";
|
8
|
+
name: "Manu Sporny"] .
|
@@ -2,5 +2,5 @@
|
|
2
2
|
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
3
3
|
|
4
4
|
<http://example.org/people#joebob> a foaf:Person;
|
5
|
-
foaf:name "Joe Bob"
|
6
|
-
foaf:nick ("joe"
|
5
|
+
foaf:name "Joe Bob";
|
6
|
+
foaf:nick ("joe" "bob" "jaybe") .
|