0
# RDF Containers and Collections
1
2
Support for RDF containers (Bag, Seq, Alt) and collections (rdf:List) providing ordered and unordered groupings of RDF resources according to RDF specifications.
3
4
## Capabilities
5
6
### Container - Base RDF Container
7
8
Base class for RDF containers with common functionality for managing collections of resources.
9
10
```python { .api }
11
class Container:
12
def __init__(self, graph: Graph, uri: Node, seq: List[Node] = None, rtype: str = "Bag"):
13
"""
14
Create an RDF container.
15
16
Parameters:
17
- graph: Graph to store container in
18
- uri: Container URI (BNode or URIRef)
19
- seq: Initial sequence of items
20
- rtype: Container type ("Bag", "Seq", "Alt")
21
"""
22
23
def append(self, item: Node) -> 'Container':
24
"""
25
Add an item to the container.
26
27
Parameters:
28
- item: Item to add
29
30
Returns:
31
Container: Self for method chaining
32
"""
33
34
def __getitem__(self, index: int) -> Node:
35
"""
36
Get item by index (1-based).
37
38
Parameters:
39
- index: Item index (starting from 1)
40
41
Returns:
42
Node: Item at index
43
"""
44
45
def __setitem__(self, index: int, item: Node):
46
"""
47
Set item at index (1-based).
48
49
Parameters:
50
- index: Item index (starting from 1)
51
- item: New item value
52
"""
53
54
def __delitem__(self, index: int):
55
"""
56
Delete item at index (1-based).
57
58
Parameters:
59
- index: Item index to delete
60
"""
61
62
def __iter__(self) -> Iterator[Node]:
63
"""
64
Iterate over container items.
65
66
Returns:
67
Iterator: Container items
68
"""
69
70
def __len__(self) -> int:
71
"""
72
Get number of items in container.
73
74
Returns:
75
int: Item count
76
"""
77
78
def index(self, item: Node) -> int:
79
"""
80
Find index of item (1-based).
81
82
Parameters:
83
- item: Item to find
84
85
Returns:
86
int: Item index or raises ValueError
87
"""
88
89
def clear(self):
90
"""Remove all items from container."""
91
92
@property
93
def uri(self) -> Node:
94
"""
95
Get container URI.
96
97
Returns:
98
Node: Container identifier
99
"""
100
101
@property
102
def graph(self) -> Graph:
103
"""
104
Get container's graph.
105
106
Returns:
107
Graph: Graph containing container
108
"""
109
```
110
111
### Bag - Unordered Container
112
113
RDF Bag container for unordered collections where duplicates are allowed.
114
115
```python { .api }
116
class Bag(Container):
117
def __init__(self, graph: Graph, uri: Node, seq: List[Node] = None):
118
"""
119
Create an RDF Bag.
120
121
Parameters:
122
- graph: Graph to store bag in
123
- uri: Bag URI (BNode or URIRef)
124
- seq: Initial sequence of items
125
"""
126
```
127
128
### Seq - Ordered Container
129
130
RDF Sequence container for ordered collections where duplicates are allowed.
131
132
```python { .api }
133
class Seq(Container):
134
def __init__(self, graph: Graph, uri: Node, seq: List[Node] = None):
135
"""
136
Create an RDF Sequence.
137
138
Parameters:
139
- graph: Graph to store sequence in
140
- uri: Sequence URI (BNode or URIRef)
141
- seq: Initial sequence of items
142
"""
143
```
144
145
### Alt - Alternative Container
146
147
RDF Alternative container representing a set of alternative values.
148
149
```python { .api }
150
class Alt(Container):
151
def __init__(self, graph: Graph, uri: Node, seq: List[Node] = None):
152
"""
153
Create an RDF Alternative.
154
155
Parameters:
156
- graph: Graph to store alternative in
157
- uri: Alternative URI (BNode or URIRef)
158
- seq: Initial sequence of alternatives
159
"""
160
```
161
162
### Collection - RDF List
163
164
Implementation of RDF collections (rdf:List) for ordered sequences.
165
166
```python { .api }
167
class Collection:
168
def __init__(self, graph: Graph, uri: Node, seq: List[Node] = None):
169
"""
170
Create an RDF Collection (List).
171
172
Parameters:
173
- graph: Graph to store collection in
174
- uri: Collection head URI (BNode or URIRef)
175
- seq: Initial sequence of items
176
"""
177
178
def append(self, item: Node) -> 'Collection':
179
"""
180
Add an item to the end of the collection.
181
182
Parameters:
183
- item: Item to add
184
185
Returns:
186
Collection: Self for method chaining
187
"""
188
189
def clear(self):
190
"""Remove all items, making this an empty list (rdf:nil)."""
191
192
def __iter__(self) -> Iterator[Node]:
193
"""
194
Iterate over collection items.
195
196
Returns:
197
Iterator: Collection items in order
198
"""
199
200
def __len__(self) -> int:
201
"""
202
Get number of items in collection.
203
204
Returns:
205
int: Item count
206
"""
207
208
def __getitem__(self, index: int) -> Node:
209
"""
210
Get item by index (0-based).
211
212
Parameters:
213
- index: Item index (starting from 0)
214
215
Returns:
216
Node: Item at index
217
"""
218
219
def __contains__(self, item: Node) -> bool:
220
"""
221
Check if collection contains item.
222
223
Parameters:
224
- item: Item to check
225
226
Returns:
227
bool: True if item is in collection
228
"""
229
230
@property
231
def uri(self) -> Node:
232
"""
233
Get collection head URI.
234
235
Returns:
236
Node: Collection head identifier
237
"""
238
239
@property
240
def graph(self) -> Graph:
241
"""
242
Get collection's graph.
243
244
Returns:
245
Graph: Graph containing collection
246
"""
247
```
248
249
## Exception Classes
250
251
```python { .api }
252
class NoElementException(Exception):
253
"""Raised when trying to access non-existent container element."""
254
```
255
256
## Usage Examples
257
258
### Working with RDF Bags
259
260
```python
261
from rdflib import Graph, BNode, Literal, URIRef
262
from rdflib.container import Bag
263
from rdflib.namespace import RDF
264
265
g = Graph()
266
267
# Create a bag
268
bag_uri = BNode()
269
fruits = Bag(g, bag_uri, [Literal("Apple"), Literal("Orange"), Literal("Banana")])
270
271
# Add more items
272
fruits.append(Literal("Grape"))
273
fruits.append(Literal("Apple")) # Duplicates allowed
274
275
# Access items
276
print(f"First fruit: {fruits[1]}") # 1-based indexing
277
print(f"Bag has {len(fruits)} items")
278
279
# Iterate over items
280
for fruit in fruits:
281
print(f"Fruit: {fruit}")
282
283
# Check the RDF representation
284
print(g.serialize(format="turtle"))
285
```
286
287
### Working with RDF Sequences
288
289
```python
290
from rdflib import Graph, BNode, Literal
291
from rdflib.container import Seq
292
293
g = Graph()
294
295
# Create a sequence (order matters)
296
seq_uri = BNode()
297
steps = Seq(g, seq_uri)
298
299
# Add steps in order
300
steps.append(Literal("Mix ingredients"))
301
steps.append(Literal("Heat oven to 350°F"))
302
steps.append(Literal("Bake for 30 minutes"))
303
steps.append(Literal("Cool before serving"))
304
305
# Access by index
306
print(f"Step 2: {steps[2]}")
307
308
# Modify a step
309
steps[2] = Literal("Preheat oven to 350°F")
310
311
# Insert is achieved by manipulating the sequence
312
print(f"Recipe has {len(steps)} steps")
313
314
# The sequence maintains order
315
for i, step in enumerate(steps, 1):
316
print(f"Step {i}: {step}")
317
```
318
319
### Working with RDF Alternatives
320
321
```python
322
from rdflib import Graph, BNode, Literal
323
from rdflib.container import Alt
324
325
g = Graph()
326
327
# Create alternatives (representing choices)
328
alt_uri = BNode()
329
formats = Alt(g, alt_uri)
330
331
# Add format alternatives
332
formats.append(Literal("PDF"))
333
formats.append(Literal("HTML"))
334
formats.append(Literal("XML"))
335
formats.append(Literal("JSON"))
336
337
# Get preferred alternative (typically first)
338
preferred = formats[1]
339
print(f"Preferred format: {preferred}")
340
341
# List all alternatives
342
print("Available formats:")
343
for fmt in formats:
344
print(f" - {fmt}")
345
```
346
347
### Working with RDF Collections (Lists)
348
349
```python
350
from rdflib import Graph, BNode, Literal, URIRef
351
from rdflib.collection import Collection
352
from rdflib.namespace import RDF
353
354
g = Graph()
355
356
# Create a collection
357
list_uri = BNode()
358
authors = Collection(g, list_uri)
359
360
# Add authors in order
361
authors.append(Literal("Alice Smith"))
362
authors.append(Literal("Bob Jones"))
363
authors.append(Literal("Carol Brown"))
364
365
# Collections are ordered and can be accessed by index
366
print(f"First author: {authors[0]}") # 0-based indexing
367
print(f"Last author: {authors[-1]}")
368
369
# Check membership
370
if Literal("Alice Smith") in authors:
371
print("Alice Smith is an author")
372
373
# Iterate over authors
374
for author in authors:
375
print(f"Author: {author}")
376
377
# Collections generate proper rdf:List structure
378
print(g.serialize(format="turtle"))
379
```
380
381
### Container Integration with Graphs
382
383
```python
384
from rdflib import Graph, URIRef, Literal
385
from rdflib.container import Bag, Seq
386
from rdflib.namespace import RDF, RDFS
387
388
g = Graph()
389
390
# Create a book with multiple authors (bag) and chapters (sequence)
391
book = URIRef("http://example.org/book/1")
392
g.add((book, RDF.type, URIRef("http://example.org/Book")))
393
g.add((book, RDFS.label, Literal("Python Programming")))
394
395
# Authors as a bag (unordered)
396
authors_bag = Bag(g, URIRef("http://example.org/book/1/authors"))
397
authors_bag.append(Literal("John Doe"))
398
authors_bag.append(Literal("Jane Smith"))
399
g.add((book, URIRef("http://example.org/hasAuthors"), authors_bag.uri))
400
401
# Chapters as a sequence (ordered)
402
chapters_seq = Seq(g, URIRef("http://example.org/book/1/chapters"))
403
chapters_seq.append(Literal("Introduction"))
404
chapters_seq.append(Literal("Basic Syntax"))
405
chapters_seq.append(Literal("Data Structures"))
406
chapters_seq.append(Literal("Functions"))
407
g.add((book, URIRef("http://example.org/hasChapters"), chapters_seq.uri))
408
409
# Query the containers
410
query = """
411
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
412
SELECT ?book ?author ?chapter WHERE {
413
?book <http://example.org/hasAuthors> ?authors .
414
?authors ?author_prop ?author .
415
FILTER(STRSTARTS(STR(?author_prop), STR(rdf:_)))
416
417
?book <http://example.org/hasChapters> ?chapters .
418
?chapters ?chapter_prop ?chapter .
419
FILTER(STRSTARTS(STR(?chapter_prop), STR(rdf:_)))
420
}
421
"""
422
423
results = g.query(query)
424
for row in results:
425
print(f"Book: {row.book}, Author: {row.author}, Chapter: {row.chapter}")
426
```
427
428
### Advanced Container Operations
429
430
```python
431
from rdflib import Graph, BNode, Literal
432
from rdflib.container import Seq
433
434
g = Graph()
435
436
# Create a mutable sequence
437
playlist = Seq(g, BNode())
438
439
# Build playlist
440
songs = [
441
"Song A",
442
"Song B",
443
"Song C",
444
"Song D"
445
]
446
447
for song in songs:
448
playlist.append(Literal(song))
449
450
# Rearrange playlist - remove and insert
451
song_b = playlist[2] # Get "Song B"
452
del playlist[2] # Remove "Song B"
453
playlist.append(song_b) # Add to end
454
455
print("Rearranged playlist:")
456
for i, song in enumerate(playlist, 1):
457
print(f"{i}. {song}")
458
459
# Find position of a song
460
try:
461
pos = playlist.index(Literal("Song A"))
462
print(f"Song A is at position {pos}")
463
except ValueError:
464
print("Song A not found")
465
466
# Clear playlist
467
playlist.clear()
468
print(f"Playlist now has {len(playlist)} songs")
469
```
470
471
### Working with Empty Containers
472
473
```python
474
from rdflib import Graph, BNode
475
from rdflib.container import Bag
476
from rdflib.collection import Collection
477
from rdflib.namespace import RDF
478
479
g = Graph()
480
481
# Create empty containers
482
empty_bag = Bag(g, BNode())
483
empty_list = Collection(g, BNode())
484
485
print(f"Empty bag length: {len(empty_bag)}")
486
print(f"Empty list length: {len(empty_list)}")
487
488
# Empty containers still create RDF structure
489
print("Empty bag RDF:")
490
for triple in g.triples((empty_bag.uri, None, None)):
491
print(f" {triple}")
492
493
print("Empty list RDF:")
494
for triple in g.triples((empty_list.uri, None, None)):
495
print(f" {triple}")
496
```