rdfxml.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. """
  2. An RDF/XML parser for RDFLib
  3. """
  4. from __future__ import annotations
  5. from typing import TYPE_CHECKING, Any, Dict, List, NoReturn, Optional, Tuple
  6. from urllib.parse import urldefrag, urljoin
  7. from xml.sax import handler, make_parser, xmlreader
  8. from xml.sax.handler import ErrorHandler
  9. from xml.sax.saxutils import escape, quoteattr
  10. from rdflib.exceptions import Error, ParserError
  11. from rdflib.graph import Graph
  12. from rdflib.namespace import RDF, is_ncname
  13. from rdflib.parser import InputSource, Parser
  14. from rdflib.plugins.parsers.RDFVOC import RDFVOC
  15. from rdflib.term import BNode, Identifier, Literal, URIRef
  16. if TYPE_CHECKING:
  17. # from xml.sax.expatreader import ExpatLocator
  18. from xml.sax.xmlreader import AttributesImpl, Locator
  19. from rdflib.graph import _ObjectType, _SubjectType, _TripleType
  20. __all__ = ["create_parser", "BagID", "ElementHandler", "RDFXMLHandler", "RDFXMLParser"]
  21. RDFNS = RDFVOC
  22. # http://www.w3.org/TR/rdf-syntax-grammar/#eventterm-attribute-URI
  23. # A mapping from unqualified terms to their qualified version.
  24. UNQUALIFIED = {
  25. "about": RDFVOC.about,
  26. "ID": RDFVOC.ID,
  27. "type": RDFVOC.type,
  28. "resource": RDFVOC.resource,
  29. "parseType": RDFVOC.parseType,
  30. }
  31. # http://www.w3.org/TR/rdf-syntax-grammar/#coreSyntaxTerms
  32. CORE_SYNTAX_TERMS = [
  33. RDFVOC.RDF,
  34. RDFVOC.ID,
  35. RDFVOC.about,
  36. RDFVOC.parseType,
  37. RDFVOC.resource,
  38. RDFVOC.nodeID,
  39. RDFVOC.datatype,
  40. ]
  41. # http://www.w3.org/TR/rdf-syntax-grammar/#syntaxTerms
  42. SYNTAX_TERMS = CORE_SYNTAX_TERMS + [RDFVOC.Description, RDFVOC.li]
  43. # http://www.w3.org/TR/rdf-syntax-grammar/#oldTerms
  44. OLD_TERMS = [
  45. URIRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#aboutEach"),
  46. URIRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#aboutEachPrefix"),
  47. URIRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#bagID"),
  48. ]
  49. NODE_ELEMENT_EXCEPTIONS = (
  50. CORE_SYNTAX_TERMS
  51. + [
  52. RDFVOC.li,
  53. ]
  54. + OLD_TERMS
  55. )
  56. NODE_ELEMENT_ATTRIBUTES = [RDFVOC.ID, RDFVOC.nodeID, RDFVOC.about]
  57. PROPERTY_ELEMENT_EXCEPTIONS = (
  58. CORE_SYNTAX_TERMS
  59. + [
  60. RDFVOC.Description,
  61. ]
  62. + OLD_TERMS
  63. )
  64. PROPERTY_ATTRIBUTE_EXCEPTIONS = (
  65. CORE_SYNTAX_TERMS + [RDFVOC.Description, RDFVOC.li] + OLD_TERMS
  66. )
  67. PROPERTY_ELEMENT_ATTRIBUTES = [RDFVOC.ID, RDFVOC.resource, RDFVOC.nodeID]
  68. XMLNS = "http://www.w3.org/XML/1998/namespace"
  69. BASE = (XMLNS, "base")
  70. LANG = (XMLNS, "lang")
  71. class BagID(URIRef):
  72. __slots__ = ["li"]
  73. def __init__(self, val):
  74. # type error: Too many arguments for "__init__" of "object"
  75. super(URIRef, self).__init__(val) # type: ignore[call-arg]
  76. self.li = 0
  77. def next_li(self):
  78. self.li += 1
  79. # type error: Type expected within [...]
  80. return RDFNS["_%s" % self.li] # type: ignore[misc]
  81. class ElementHandler:
  82. __slots__ = [
  83. "start",
  84. "char",
  85. "end",
  86. "li",
  87. "id",
  88. "base",
  89. "subject",
  90. "predicate",
  91. "object",
  92. "list",
  93. "language",
  94. "datatype",
  95. "declared",
  96. "data",
  97. ]
  98. def __init__(self):
  99. self.start = None
  100. self.char = None
  101. self.end = None
  102. self.li = 0
  103. self.id = None
  104. self.base = None
  105. self.subject = None
  106. self.object = None
  107. self.list = None
  108. self.language = None
  109. self.datatype = None
  110. self.declared = None
  111. self.data = None
  112. def next_li(self):
  113. self.li += 1
  114. return RDFVOC["_%s" % self.li]
  115. class RDFXMLHandler(handler.ContentHandler):
  116. def __init__(self, store: Graph):
  117. self.store = store
  118. self.preserve_bnode_ids = False
  119. self.reset()
  120. def reset(self) -> None:
  121. document_element = ElementHandler()
  122. document_element.start = self.document_element_start
  123. document_element.end = lambda name, qname: None
  124. self.stack: List[Optional[ElementHandler]] = [
  125. None,
  126. document_element,
  127. ]
  128. self.ids: Dict[str, int] = {} # remember IDs we have already seen
  129. self.bnode: Dict[str, Identifier] = {}
  130. self._ns_contexts: List[Dict[str, Optional[str]]] = [
  131. {}
  132. ] # contains uri -> prefix dicts
  133. self._current_context: Dict[str, Optional[str]] = self._ns_contexts[-1]
  134. # ContentHandler methods
  135. def setDocumentLocator(self, locator: Locator):
  136. self.locator = locator
  137. def startDocument(self) -> None:
  138. pass
  139. def startPrefixMapping(self, prefix: Optional[str], namespace: str) -> None:
  140. self._ns_contexts.append(self._current_context.copy())
  141. self._current_context[namespace] = prefix
  142. self.store.bind(prefix, namespace or "", override=False)
  143. def endPrefixMapping(self, prefix: Optional[str]) -> None:
  144. self._current_context = self._ns_contexts[-1]
  145. del self._ns_contexts[-1]
  146. def startElementNS(
  147. self, name: Tuple[Optional[str], str], qname, attrs: AttributesImpl
  148. ) -> None:
  149. stack = self.stack
  150. stack.append(ElementHandler())
  151. current = self.current
  152. parent = self.parent
  153. # type error: No overlaod for "get" of "AttributesImpl" mactches tuple (str, str)
  154. base = attrs.get(BASE, None) # type: ignore[call-overload, unused-ignore]
  155. if base is not None:
  156. base, frag = urldefrag(base)
  157. if parent and parent.base:
  158. base = urljoin(parent.base, base)
  159. else:
  160. systemId = self.locator.getPublicId() or self.locator.getSystemId()
  161. if systemId:
  162. base = urljoin(systemId, base)
  163. else:
  164. if parent:
  165. base = parent.base
  166. if base is None:
  167. systemId = self.locator.getPublicId() or self.locator.getSystemId()
  168. if systemId:
  169. base, frag = urldefrag(systemId)
  170. current.base = base
  171. # type error: No overlaod for "get" of "AttributesImpl" mactches tuple (str, str)
  172. language = attrs.get(LANG, None) # type: ignore[call-overload, unused-ignore]
  173. if language is None:
  174. if parent:
  175. language = parent.language
  176. current.language = language
  177. current.start(name, qname, attrs)
  178. def endElementNS(self, name: Tuple[Optional[str], str], qname) -> None:
  179. self.current.end(name, qname)
  180. self.stack.pop()
  181. def characters(self, content: str) -> None:
  182. char = self.current.char
  183. if char:
  184. char(content)
  185. def ignorableWhitespace(self, content) -> None:
  186. pass
  187. def processingInstruction(self, target, data) -> None:
  188. pass
  189. def add_reified(self, sid: Identifier, spo: _TripleType):
  190. s, p, o = spo
  191. self.store.add((sid, RDF.type, RDF.Statement))
  192. self.store.add((sid, RDF.subject, s))
  193. self.store.add((sid, RDF.predicate, p))
  194. self.store.add((sid, RDF.object, o))
  195. def error(self, message: str) -> NoReturn:
  196. locator = self.locator
  197. info = "%s:%s:%s: " % (
  198. locator.getSystemId(),
  199. locator.getLineNumber(),
  200. locator.getColumnNumber(),
  201. )
  202. raise ParserError(info + message)
  203. def get_current(self) -> Optional[ElementHandler]:
  204. return self.stack[-2]
  205. # Create a read only property called current so that self.current
  206. # give the current element handler.
  207. current = property(get_current)
  208. def get_next(self) -> Optional[ElementHandler]:
  209. return self.stack[-1]
  210. # Create a read only property that gives the element handler to be
  211. # used for the next element.
  212. next = property(get_next)
  213. def get_parent(self) -> Optional[ElementHandler]:
  214. return self.stack[-3]
  215. # Create a read only property that gives the current parent
  216. # element handler
  217. parent = property(get_parent)
  218. def absolutize(self, uri: str) -> URIRef:
  219. # type error: Argument "allow_fragments" to "urljoin" has incompatible type "int"; expected "bool"
  220. result = urljoin(self.current.base, uri, allow_fragments=1) # type: ignore[arg-type]
  221. if uri and uri[-1] == "#" and result[-1] != "#":
  222. result = "%s#" % result
  223. return URIRef(result)
  224. def convert(
  225. self, name: Tuple[Optional[str], str], qname, attrs: AttributesImpl
  226. ) -> Tuple[URIRef, Dict[URIRef, str]]:
  227. if name[0] is None:
  228. # type error: Incompatible types in assignment (expression has type "URIRef", variable has type "Tuple[Optional[str], str]")
  229. name = URIRef(name[1]) # type: ignore[assignment]
  230. else:
  231. # type error: Incompatible types in assignment (expression has type "URIRef", variable has type "Tuple[Optional[str], str]")
  232. # type error: Argument 1 to "join" of "str" has incompatible type "Tuple[Optional[str], str]"; expected "Iterable[str]"
  233. name = URIRef("".join(name)) # type: ignore[assignment, arg-type]
  234. atts = {}
  235. for n, v in attrs.items():
  236. # mypy error: mypy thinks n[0]==None is unreachable
  237. if n[0] is None:
  238. att = n[1] # type: ignore[unreachable, unused-ignore]
  239. else:
  240. att = "".join(n)
  241. if att.startswith(XMLNS) or att[0:3].lower() == "xml":
  242. pass
  243. elif att in UNQUALIFIED:
  244. # if not RDFNS[att] in atts:
  245. # type error: Variable "att" is not valid as a type
  246. atts[RDFNS[att]] = v # type: ignore[misc, valid-type]
  247. else:
  248. atts[URIRef(att)] = v
  249. # type error: Incompatible return value type (got "Tuple[Tuple[Optional[str], str], Dict[Any, Any]]", expected "Tuple[URIRef, Dict[URIRef, str]]")
  250. return name, atts # type: ignore[return-value]
  251. def document_element_start(
  252. self, name: Tuple[str, str], qname, attrs: AttributesImpl
  253. ) -> None:
  254. if name[0] and URIRef("".join(name)) == RDFVOC.RDF:
  255. # Cheap hack so 2to3 doesn't turn it into __next__
  256. next = getattr(self, "next")
  257. next.start = self.node_element_start
  258. next.end = self.node_element_end
  259. else:
  260. self.node_element_start(name, qname, attrs)
  261. # self.current.end = self.node_element_end
  262. # TODO... set end to something that sets start such that
  263. # another element will cause error
  264. def node_element_start(
  265. self, name: Tuple[str, str], qname, attrs: AttributesImpl
  266. ) -> None:
  267. # type error: Incompatible types in assignment (expression has type "URIRef", variable has type "Tuple[str, str]")
  268. name, atts = self.convert(name, qname, attrs) # type: ignore[assignment]
  269. current = self.current
  270. absolutize = self.absolutize
  271. # Cheap hack so 2to3 doesn't turn it into __next__
  272. next = getattr(self, "next")
  273. next.start = self.property_element_start
  274. next.end = self.property_element_end
  275. if name in NODE_ELEMENT_EXCEPTIONS:
  276. # type error: Not all arguments converted during string formatting
  277. self.error("Invalid node element URI: %s" % name) # type: ignore[str-format]
  278. subject: _SubjectType
  279. if RDFVOC.ID in atts:
  280. if RDFVOC.about in atts or RDFVOC.nodeID in atts:
  281. self.error("Can have at most one of rdf:ID, rdf:about, and rdf:nodeID")
  282. id = atts[RDFVOC.ID]
  283. if not is_ncname(id):
  284. self.error("rdf:ID value is not a valid NCName: %s" % id)
  285. subject = absolutize("#%s" % id)
  286. if subject in self.ids:
  287. self.error("two elements cannot use the same ID: '%s'" % subject)
  288. self.ids[subject] = 1 # IDs can only appear once within a document
  289. elif RDFVOC.nodeID in atts:
  290. if RDFVOC.ID in atts or RDFVOC.about in atts:
  291. self.error("Can have at most one of rdf:ID, rdf:about, and rdf:nodeID")
  292. nodeID = atts[RDFVOC.nodeID]
  293. if not is_ncname(nodeID):
  294. self.error("rdf:nodeID value is not a valid NCName: %s" % nodeID)
  295. if self.preserve_bnode_ids is False:
  296. if nodeID in self.bnode:
  297. subject = self.bnode[nodeID]
  298. else:
  299. subject = BNode()
  300. self.bnode[nodeID] = subject
  301. else:
  302. subject = BNode(nodeID)
  303. elif RDFVOC.about in atts:
  304. if RDFVOC.ID in atts or RDFVOC.nodeID in atts:
  305. self.error("Can have at most one of rdf:ID, rdf:about, and rdf:nodeID")
  306. subject = absolutize(atts[RDFVOC.about])
  307. else:
  308. subject = BNode()
  309. if name != RDFVOC.Description: # S1
  310. # error: Argument 1 has incompatible type "Tuple[str, str]"; expected "str"
  311. self.store.add((subject, RDF.type, absolutize(name))) # type: ignore[arg-type]
  312. object: _ObjectType
  313. language = current.language
  314. for att in atts:
  315. if not att.startswith(str(RDFNS)):
  316. predicate = absolutize(att)
  317. try:
  318. object = Literal(atts[att], language)
  319. except Error as e:
  320. # type error: Argument 1 to "error" of "RDFXMLHandler" has incompatible type "Optional[str]"; expected "str"
  321. self.error(e.msg) # type: ignore[arg-type]
  322. elif att == RDF.type: # S2
  323. predicate = RDF.type
  324. object = absolutize(atts[RDF.type])
  325. elif att in NODE_ELEMENT_ATTRIBUTES:
  326. continue
  327. elif att in PROPERTY_ATTRIBUTE_EXCEPTIONS: # S3
  328. self.error("Invalid property attribute URI: %s" % att)
  329. # type error: Statement is unreachable
  330. continue # type: ignore[unreachable] # for when error does not throw an exception
  331. else:
  332. predicate = absolutize(att)
  333. try:
  334. object = Literal(atts[att], language)
  335. except Error as e:
  336. # type error: Argument 1 to "error" of "RDFXMLHandler" has incompatible type "Optional[str]"; expected "str"
  337. self.error(e.msg) # type: ignore[arg-type]
  338. self.store.add((subject, predicate, object))
  339. current.subject = subject
  340. def node_element_end(self, name: Tuple[str, str], qname) -> None:
  341. # repeat node-elements are only allowed
  342. # at at top-level
  343. if self.parent.object and self.current != self.stack[2]:
  344. self.error(
  345. "Repeat node-elements inside property elements: %s" % "".join(name)
  346. )
  347. self.parent.object = self.current.subject
  348. def property_element_start(
  349. self, name: Tuple[str, str], qname, attrs: AttributesImpl
  350. ) -> None:
  351. # type error: Incompatible types in assignment (expression has type "URIRef", variable has type "Tuple[str, str]")
  352. name, atts = self.convert(name, qname, attrs) # type: ignore[assignment]
  353. current = self.current
  354. absolutize = self.absolutize
  355. # Cheap hack so 2to3 doesn't turn it into __next__
  356. next = getattr(self, "next")
  357. object: Optional[_ObjectType] = None
  358. current.data = None
  359. current.list = None
  360. # type error: "Tuple[str, str]" has no attribute "startswith"
  361. if not name.startswith(str(RDFNS)): # type: ignore[attr-defined]
  362. # type error: Argument 1 has incompatible type "Tuple[str, str]"; expected "str"
  363. current.predicate = absolutize(name) # type: ignore[arg-type]
  364. elif name == RDFVOC.li:
  365. current.predicate = current.next_li()
  366. elif name in PROPERTY_ELEMENT_EXCEPTIONS:
  367. # type error: Not all arguments converted during string formatting
  368. self.error("Invalid property element URI: %s" % name) # type: ignore[str-format]
  369. else:
  370. # type error: Argument 1 has incompatible type "Tuple[str, str]"; expected "str"
  371. current.predicate = absolutize(name) # type: ignore[arg-type]
  372. id = atts.get(RDFVOC.ID, None)
  373. if id is not None:
  374. if not is_ncname(id):
  375. self.error("rdf:ID value is not a value NCName: %s" % id)
  376. current.id = absolutize("#%s" % id)
  377. else:
  378. current.id = None
  379. resource = atts.get(RDFVOC.resource, None)
  380. nodeID = atts.get(RDFVOC.nodeID, None)
  381. parse_type = atts.get(RDFVOC.parseType, None)
  382. if resource is not None and nodeID is not None:
  383. self.error("Property element cannot have both rdf:nodeID and rdf:resource")
  384. if resource is not None:
  385. object = absolutize(resource)
  386. next.start = self.node_element_start
  387. next.end = self.node_element_end
  388. elif nodeID is not None:
  389. if not is_ncname(nodeID):
  390. self.error("rdf:nodeID value is not a valid NCName: %s" % nodeID)
  391. if self.preserve_bnode_ids is False:
  392. if nodeID in self.bnode:
  393. object = self.bnode[nodeID]
  394. else:
  395. subject = BNode()
  396. self.bnode[nodeID] = subject
  397. object = subject
  398. else:
  399. object = subject = BNode(nodeID)
  400. next.start = self.node_element_start
  401. next.end = self.node_element_end
  402. else:
  403. if parse_type is not None:
  404. for att in atts:
  405. if att != RDFVOC.parseType and att != RDFVOC.ID:
  406. self.error("Property attr '%s' now allowed here" % att)
  407. if parse_type == "Resource":
  408. current.subject = object = BNode()
  409. current.char = self.property_element_char
  410. next.start = self.property_element_start
  411. next.end = self.property_element_end
  412. elif parse_type == "Collection":
  413. current.char = None
  414. object = current.list = RDF.nil # BNode()
  415. # self.parent.subject
  416. next.start = self.node_element_start
  417. next.end = self.list_node_element_end
  418. else: # if parse_type=="Literal":
  419. # All other values are treated as Literal
  420. # See: http://www.w3.org/TR/rdf-syntax-grammar/
  421. # parseTypeOtherPropertyElt
  422. object = Literal("", datatype=RDFVOC.XMLLiteral)
  423. current.char = self.literal_element_char
  424. current.declared = {XMLNS: "xml"}
  425. next.start = self.literal_element_start
  426. next.char = self.literal_element_char
  427. next.end = self.literal_element_end
  428. current.object = object
  429. return
  430. else:
  431. object = None
  432. current.char = self.property_element_char
  433. next.start = self.node_element_start
  434. next.end = self.node_element_end
  435. datatype = current.datatype = atts.get(RDFVOC.datatype, None)
  436. language = current.language
  437. if datatype is not None:
  438. # TODO: check that there are no atts other than datatype and id
  439. datatype = absolutize(datatype)
  440. else:
  441. for att in atts:
  442. if not att.startswith(str(RDFNS)):
  443. predicate = absolutize(att)
  444. elif att in PROPERTY_ELEMENT_ATTRIBUTES:
  445. continue
  446. elif att in PROPERTY_ATTRIBUTE_EXCEPTIONS:
  447. self.error("""Invalid property attribute URI: %s""" % att)
  448. else:
  449. predicate = absolutize(att)
  450. o: _ObjectType
  451. if att == RDF.type:
  452. o = URIRef(atts[att])
  453. else:
  454. if datatype is not None:
  455. # type error: Statement is unreachable
  456. language = None # type: ignore[unreachable]
  457. o = Literal(atts[att], language, datatype)
  458. if object is None:
  459. object = BNode()
  460. self.store.add((object, predicate, o))
  461. if object is None:
  462. current.data = ""
  463. current.object = None
  464. else:
  465. current.data = None
  466. current.object = object
  467. def property_element_char(self, data: str) -> None:
  468. current = self.current
  469. if current.data is not None:
  470. current.data += data
  471. def property_element_end(self, name: Tuple[str, str], qname) -> None:
  472. current = self.current
  473. if current.data is not None and current.object is None:
  474. literalLang = current.language
  475. if current.datatype is not None:
  476. literalLang = None
  477. current.object = Literal(current.data, literalLang, current.datatype)
  478. current.data = None
  479. if self.next.end == self.list_node_element_end:
  480. if current.object != RDF.nil:
  481. self.store.add((current.list, RDF.rest, RDF.nil))
  482. if current.object is not None:
  483. self.store.add((self.parent.subject, current.predicate, current.object))
  484. if current.id is not None:
  485. self.add_reified(
  486. current.id, (self.parent.subject, current.predicate, current.object)
  487. )
  488. current.subject = None
  489. def list_node_element_end(self, name: Tuple[str, str], qname) -> None:
  490. current = self.current
  491. if self.parent.list == RDF.nil:
  492. list = BNode()
  493. # Removed between 20030123 and 20030905
  494. # self.store.add((list, RDF.type, LIST))
  495. self.parent.list = list
  496. self.store.add((self.parent.list, RDF.first, current.subject))
  497. self.parent.object = list
  498. self.parent.char = None
  499. else:
  500. list = BNode()
  501. # Removed between 20030123 and 20030905
  502. # self.store.add((list, RDF.type, LIST))
  503. self.store.add((self.parent.list, RDF.rest, list))
  504. self.store.add((list, RDF.first, current.subject))
  505. self.parent.list = list
  506. def literal_element_start(
  507. self, name: Tuple[str, str], qname, attrs: AttributesImpl
  508. ) -> None:
  509. current = self.current
  510. self.next.start = self.literal_element_start
  511. self.next.char = self.literal_element_char
  512. self.next.end = self.literal_element_end
  513. current.declared = self.parent.declared.copy()
  514. if name[0]:
  515. prefix = self._current_context[name[0]]
  516. if prefix:
  517. current.object = "<%s:%s" % (prefix, name[1])
  518. else:
  519. current.object = "<%s" % name[1]
  520. if not name[0] in current.declared: # noqa: E713
  521. current.declared[name[0]] = prefix
  522. if prefix:
  523. current.object += ' xmlns:%s="%s"' % (prefix, name[0])
  524. else:
  525. current.object += ' xmlns="%s"' % name[0]
  526. else:
  527. current.object = "<%s" % name[1]
  528. # type error: Incompatible types in assignment (expression has type "str", variable has type "Tuple[str, str]")
  529. for name, value in attrs.items(): # type: ignore[assignment, unused-ignore]
  530. if name[0]:
  531. if not name[0] in current.declared: # noqa: E713
  532. current.declared[name[0]] = self._current_context[name[0]]
  533. name = current.declared[name[0]] + ":" + name[1]
  534. else:
  535. # type error: Incompatible types in assignment (expression has type "str", variable has type "Tuple[str, str]")
  536. name = name[1] # type: ignore[assignment]
  537. current.object += " %s=%s" % (name, quoteattr(value))
  538. current.object += ">"
  539. def literal_element_char(self, data: str) -> None:
  540. self.current.object += escape(data)
  541. def literal_element_end(self, name: Tuple[str, str], qname) -> None:
  542. if name[0]:
  543. prefix = self._current_context[name[0]]
  544. if prefix:
  545. end = "</%s:%s>" % (prefix, name[1])
  546. else:
  547. end = "</%s>" % name[1]
  548. else:
  549. end = "</%s>" % name[1]
  550. self.parent.object += self.current.object + end
  551. def create_parser(target: InputSource, store: Graph) -> xmlreader.XMLReader:
  552. parser = make_parser()
  553. try:
  554. # Workaround for bug in expatreader.py. Needed when
  555. # expatreader is trying to guess a prefix.
  556. parser.start_namespace_decl("xml", "http://www.w3.org/XML/1998/namespace") # type: ignore[attr-defined]
  557. except AttributeError:
  558. pass # Not present in Jython (at least)
  559. parser.setFeature(handler.feature_namespaces, 1)
  560. rdfxml = RDFXMLHandler(store)
  561. # type error: Argument 1 to "setDocumentLocator" of "RDFXMLHandler" has incompatible type "InputSource"; expected "Locator"
  562. rdfxml.setDocumentLocator(target) # type: ignore[arg-type]
  563. # rdfxml.setDocumentLocator(_Locator(self.url, self.parser))
  564. parser.setContentHandler(rdfxml)
  565. parser.setErrorHandler(ErrorHandler())
  566. return parser
  567. class RDFXMLParser(Parser):
  568. """An RDF/XML parser."""
  569. def __init__(self):
  570. pass
  571. def parse(self, source: InputSource, sink: Graph, **args: Any) -> None:
  572. self._parser = create_parser(source, sink)
  573. content_handler = self._parser.getContentHandler()
  574. preserve_bnode_ids = args.get("preserve_bnode_ids", None)
  575. if preserve_bnode_ids is not None:
  576. # type error: ContentHandler has no attribute "preserve_bnode_ids"
  577. content_handler.preserve_bnode_ids = preserve_bnode_ids # type: ignore[attr-defined, unused-ignore]
  578. # # We're only using it once now
  579. # content_handler.reset()
  580. # self._parser.reset()
  581. self._parser.parse(source)