def serialize(self, value: Union[date, time], **kwargs: Any) -> str: try: return value.strftime(kwargs["format"]) except KeyError: raise ConverterError("Missing format keyword argument") except Exception as e: raise ConverterError(e)
def parse(cls, value: Any, **kwargs: Any) -> datetime: try: return datetime.strptime(value, kwargs["format"]) except KeyError: raise ConverterError("Missing format keyword argument") except Exception as e: raise ConverterError(e)
def deserialize(self, value: Any, **kwargs: Any) -> bytes: self.validate_input_type(value, str) try: fmt = kwargs.get("format") if fmt == "base16": return binascii.unhexlify(value) if fmt == "base64": return base64.b64decode(value) raise ConverterError(f"Unknown format '{fmt}'") except ValueError as e: raise ConverterError(e)
def to_bool(value: Any) -> bool: val = value.strip() if val in ("true", "1"): return True if val in ("false", "0"): return False raise ConverterError(f"Invalid bool literal '{value}'")
def resolve(value: str, ns_map: Optional[Dict]) -> Tuple: if not value: raise ConverterError("Invalid QName") if value[0] == "{": return value, None if ns_map is None: raise ConverterError( "QName converter needs ns_map to support prefixes") prefix, suffix = text.split(value.strip()) namespace = ns_map.get(prefix) if prefix and not namespace: raise ConverterError(f"Unknown namespace prefix: `{prefix}`") return namespace, suffix
def to_class(clazz: Any, value: Any, ns_map: Optional[Dict]) -> Any: if clazz is QName: return to_qname(value, ns_map) if issubclass(clazz, Enum): return to_enum(clazz, value, ns_map) if is_dataclass(clazz): return clazz(value) raise ConverterError(f"Unhandled class type {clazz.__name__}")
def serialize(self, value: bytes, **kwargs: Any) -> str: fmt = kwargs.get("format") if isinstance(value, XmlHexBinary) or fmt == "base16": return base64.b16encode(value).decode() if isinstance(value, XmlBase64Binary) or fmt == "base64": return base64.b64encode(value).decode() raise ConverterError(f"Unknown format '{fmt}'")
def deserialize(self, value: Any, data_type: Optional[EnumMeta] = None, **kwargs: Any) -> Enum: if data_type is None or not isinstance(data_type, EnumMeta): raise ConverterError(f"'{data_type}' is not an enum") if isinstance(value, (list, tuple)): values = value elif isinstance(value, str): value = value.strip() values = value.split() else: values = [value] length = len(values) for member in cast(Type[Enum], data_type): if self.match(value, values, length, member.value, **kwargs): return member raise ConverterError()
def resolve(value: str, ns_map: Optional[Dict]) -> Tuple: value = value.strip() if not value: raise ConverterError() if value[0] == "{": uri, name = text.split(value[1:], "}") if not namespaces.is_uri(uri): raise ConverterError() else: prefix, name = text.split(value, ":") uri = ns_map.get(prefix) if ns_map else None if prefix and not uri: raise ConverterError(f"Unknown namespace prefix: `{prefix}`") if " " in name or not namespaces.is_ncname(name): raise ConverterError() return uri, name
def deserialize(self, value: Any, data_type: Optional[Type[Enum]] = None, **kwargs: Any) -> Enum: if data_type is None or not issubclass(data_type, Enum): raise ConverterError("Provide a target data type enum class.") # Convert string value to the type of the first enum member first, otherwise # more complex types like QName, Decimals will fail. member: Enum = list(data_type)[0] value_type = type(member.value) # Suppress warnings with warnings.catch_warnings(): warnings.simplefilter("ignore") real_value = converter.deserialize(value, [value_type], **kwargs) # Raise exception if the real value doesn't match the expected type. if not isinstance(real_value, value_type): raise ConverterError() try: # Attempt no1 use the enum constructor return data_type(real_value) except ValueError: pass try: # Attempt no2 the enum might be derived from # xs:NMTOKENS or xs:list removing excess whitespace. if isinstance(real_value, str): return data_type(" ".join(real_value.split())) # Attempt #3 some values are never equal try to match # canonical representations. repr_value = repr(real_value) return next(x for x in data_type if repr(x.value) == repr_value) except (ValueError, StopIteration): raise ConverterError()
def deserialize(self, value: Any, **kwargs: Any) -> bool: if isinstance(value, str): val = value.strip() if val in ("true", "1"): return True if val in ("false", "0"): return False raise ConverterError(f"Invalid bool literal '{value}'") return True if value else False
def to_xml(value: Any) -> Any: if value is None: return None if isinstance(value, bool): return "true" if value else "false" if isinstance(value, Enum): return str(value.value) if isinstance(value, float): return "NaN" if math.isnan(value) else str(value).upper() if isinstance(value, Decimal) and value.is_infinite(): return str(value).replace("Infinity", "INF") if isinstance(value, QName): return value if is_dataclass(value): raise ConverterError("Text nodes can't be dataclasses!") return str(value)
def deserialize(self, value: str, ns_map: Optional[Dict] = None, **kwargs: Any) -> etree.QName: """ Convert namespace prefixed strings, or fully qualified strings to QNames. examples: - xs:string -> QName("http://www.w3.org/2001/XMLSchema", "string") - {foo}bar -> QName("foo", "bar" """ try: text_or_uri, tag = QNameConverter.resolve(value, ns_map) return etree.QName(text_or_uri, tag) except ValueError: raise ConverterError()
def to_xml(value: Any, namespaces: Optional[Namespaces] = None) -> Any: if value is None: return None if isinstance(value, list): return " ".join(map(lambda x: to_xml(x, namespaces), value)) if isinstance(value, bool): return "true" if value else "false" if isinstance(value, Enum): return to_xml(value.value, namespaces) if isinstance(value, float): return "NaN" if math.isnan(value) else str(value).upper() if isinstance(value, Decimal) and value.is_infinite(): return str(value).replace("Infinity", "INF") if isinstance(value, QName): return qname_to_xml(value, namespaces) if namespaces else value.text if is_dataclass(value): raise ConverterError("Text nodes can't be dataclasses!") return str(value)
def deserialize(self, value: Any, **kwargs: Any) -> Decimal: try: return Decimal(value) except InvalidOperation: raise ConverterError()
def deserialize(self, value: Any, **kwargs: Any) -> int: try: return int(value) except (ValueError, TypeError) as e: raise ConverterError(e)
def validate_input_type(cls, value: Any, tp: Type): if not isinstance(value, tp): raise ConverterError( f"Input value must be '{tp.__name__}' got '{type(value).__name__}'" )
def deserialize(self, value: Any, **kwargs: Any) -> float: try: return float(value) except ValueError as e: raise ConverterError(e)
def deserialize(self, value: Any, **kwargs: Any) -> Any: try: return self.func(value) except ValueError as e: raise ConverterError(e)