"""XML Metadata parser superclass."""
from abc import ABC
from types import MappingProxyType
from typing import Any
import xmltodict
from marshmallow.fields import Constant
from typing_extensions import override
from comicbox.schemas.base import BaseRenderModule, BaseSchema, BaseSubSchema
XML_UNPARSE_ARGS = MappingProxyType(
# used by tests
{
# Capitalize UTF-8 to be standard.
"encoding": "UTF-8",
"pretty": True,
"short_empty_elements": True,
}
)
class XmlRenderModule(BaseRenderModule):
"""Marshmallow Render Module imitates json module."""
@override
@classmethod
def dumps(cls, obj: dict, *args: Any, **kwargs: Any) -> str:
"""Dump dict to XML string."""
return xmltodict.unparse( # ty: ignore[no-matching-overload]
obj,
*args,
**XML_UNPARSE_ARGS,
**kwargs,
)
@override
@classmethod
def loads(
cls,
s: str | bytes | bytearray,
*args: Any,
**kwargs: Any,
) -> Any:
"""Load XML string into a dict."""
if cleaned_s := cls.clean_string(s):
return xmltodict.parse(cleaned_s, *args, **kwargs)
return None
class XmlSubSchema(BaseSubSchema, ABC):
"""XML Rendered Sub Schema."""
class Meta(BaseSubSchema.Meta):
"""Schema Options."""
render_module = XmlRenderModule
def create_xml_headers(
ns: str, ns_uri: str, xsd_uri: str
) -> MappingProxyType[str, Constant]:
"""Create Namespace and Schema Location XML Attributes."""
return MappingProxyType(
{
f"@xmlns:{ns}": Constant(ns_uri),
"@xsi:schemaLocation": Constant(f"{ns_uri} {xsd_uri}"),
}
)
class XmlSubHeadSchema(XmlSubSchema, ABC):
"""XML Head Sub Schema customizations."""
class Meta(XmlSubSchema.Meta):
"""Schema Options."""
NS = ""
NS_URI = ""
XSD_URI = ""
include = MappingProxyType(
{
"@xmlns:xsd": Constant("http://www.w3.org/2001/XMLSchema"),
"@xmlns:xsi": Constant("http://www.w3.org/2001/XMLSchema-instance"),
}
)
class XmlSchema(BaseSchema, ABC):
"""Xml Rendered Schema."""
class Meta(BaseSchema.Meta):
"""Schema Options."""
render_module = XmlRenderModule