1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
|
import io
from typing import Any, List, Optional
from xsdata.exceptions import ParserError
from xsdata.models.dtd import (
Dtd,
DtdAttribute,
DtdAttributeDefault,
DtdAttributeType,
DtdContent,
DtdContentOccur,
DtdContentType,
DtdElement,
DtdElementType,
)
from xsdata.models.enums import Namespace
class DtdParser:
@classmethod
def parse(cls, source: Any, location: str) -> Dtd:
try:
from lxml import etree
dtd = etree.DTD(io.BytesIO(source))
except ImportError:
raise ParserError("DtdParser requires lxml to run.")
elements = list(map(cls.build_element, dtd.iterelements()))
return Dtd(elements=elements, location=location)
@classmethod
def build_element(cls, element: Any) -> DtdElement:
content = cls.build_content(element.content)
attributes = list(map(cls.build_attribute, element.iterattributes()))
ns_map = cls.build_ns_map(element.prefix, attributes)
return DtdElement(
name=element.name,
prefix=element.prefix,
type=DtdElementType(element.type),
content=content,
attributes=attributes,
ns_map=ns_map,
)
@classmethod
def build_content(cls, content: Any) -> Optional[DtdContent]:
if not content:
return None
return DtdContent(
name=content.name,
occur=DtdContentOccur(content.occur),
type=DtdContentType(content.type),
left=cls.build_content(content.left),
right=cls.build_content(content.right),
)
@classmethod
def build_attribute(cls, attribute: Any) -> DtdAttribute:
return DtdAttribute(
prefix=attribute.prefix,
name=attribute.name,
type=DtdAttributeType(attribute.type),
default=DtdAttributeDefault(attribute.default),
default_value=attribute.default_value,
values=attribute.values(),
)
@classmethod
def build_ns_map(cls, prefix: str, attributes: List[DtdAttribute]) -> dict:
ns_map = {ns.prefix: ns.uri for ns in Namespace.common()}
for attribute in list(attributes):
if not attribute.default_value:
continue
if attribute.prefix == "xmlns":
ns_map[attribute.name] = attribute.default_value
attributes.remove(attribute)
elif attribute.name == "xmlns":
ns_map[prefix] = attribute.default_value
attributes.remove(attribute)
return ns_map
|