trix.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. from __future__ import annotations
  2. from typing import IO, Any, Optional
  3. from rdflib.graph import ConjunctiveGraph, Graph
  4. from rdflib.namespace import Namespace
  5. from rdflib.plugins.serializers.xmlwriter import XMLWriter
  6. from rdflib.serializer import Serializer
  7. from rdflib.term import BNode, Literal, URIRef
  8. __all__ = ["TriXSerializer"]
  9. # TODO: Move this somewhere central
  10. TRIXNS = Namespace("http://www.w3.org/2004/03/trix/trix-1/")
  11. XMLNS = Namespace("http://www.w3.org/XML/1998/namespace")
  12. class TriXSerializer(Serializer):
  13. """TriX RDF graph serializer."""
  14. def __init__(self, store: Graph):
  15. super(TriXSerializer, self).__init__(store)
  16. if not store.context_aware:
  17. raise Exception(
  18. "TriX serialization only makes sense for context-aware stores"
  19. )
  20. def serialize(
  21. self,
  22. stream: IO[bytes],
  23. base: Optional[str] = None,
  24. encoding: Optional[str] = None,
  25. **kwargs: Any,
  26. ) -> None:
  27. nm = self.store.namespace_manager
  28. self.writer = XMLWriter(stream, nm, encoding, extra_ns={"": TRIXNS})
  29. self.writer.push(TRIXNS["TriX"])
  30. # if base is given here, use that, if not and a base is set for the graph use that
  31. if base is None and self.store.base is not None:
  32. base = self.store.base
  33. if base is not None:
  34. self.writer.attribute("http://www.w3.org/XML/1998/namespacebase", base)
  35. self.writer.namespaces()
  36. if isinstance(self.store, ConjunctiveGraph):
  37. for subgraph in self.store.contexts():
  38. self._writeGraph(subgraph)
  39. elif isinstance(self.store, Graph):
  40. self._writeGraph(self.store)
  41. else:
  42. raise Exception(f"Unknown graph type: {type(self.store)}")
  43. self.writer.pop()
  44. stream.write("\n".encode("latin-1"))
  45. def _writeGraph(self, graph): # noqa: N802
  46. self.writer.push(TRIXNS["graph"])
  47. if graph.base:
  48. self.writer.attribute(
  49. "http://www.w3.org/XML/1998/namespacebase", graph.base
  50. )
  51. if isinstance(graph.identifier, URIRef):
  52. self.writer.element(TRIXNS["uri"], content=str(graph.identifier))
  53. for triple in graph.triples((None, None, None)):
  54. self._writeTriple(triple)
  55. self.writer.pop()
  56. def _writeTriple(self, triple): # noqa: N802
  57. self.writer.push(TRIXNS["triple"])
  58. for component in triple:
  59. if isinstance(component, URIRef):
  60. self.writer.element(TRIXNS["uri"], content=str(component))
  61. elif isinstance(component, BNode):
  62. self.writer.element(TRIXNS["id"], content=str(component))
  63. elif isinstance(component, Literal):
  64. if component.datatype:
  65. self.writer.element(
  66. TRIXNS["typedLiteral"],
  67. content=str(component),
  68. attributes={TRIXNS["datatype"]: str(component.datatype)},
  69. )
  70. elif component.language:
  71. self.writer.element(
  72. TRIXNS["plainLiteral"],
  73. content=str(component),
  74. attributes={XMLNS["lang"]: str(component.language)},
  75. )
  76. else:
  77. self.writer.element(TRIXNS["plainLiteral"], content=str(component))
  78. self.writer.pop()