def test_unknown_converter(self): class A: pass class B(A): pass with warnings.catch_warnings(record=True) as w: converter.serialize(B()) self.assertEqual(f"No converter registered for `{B}`", str(w[-1].message))
def test_serialize(self): self.assertEqual(None, converter.serialize(None)) self.assertEqual("1", converter.serialize(1)) self.assertEqual("1 2 3", converter.serialize([1, "2", 3])) self.assertEqual(None, converter.serialize(None)) self.assertEqual("1", converter.serialize(1)) self.assertEqual("1.5", converter.serialize(1.5)) self.assertEqual("true", converter.serialize(True)) self.assertEqual("optional", converter.serialize(UseType.OPTIONAL)) self.assertEqual("8.77683E-8", converter.serialize(Decimal("8.77683E-8"))) self.assertEqual("8.77683E-08", converter.serialize(float("8.77683E-8")))
def encode_data(self, data: Any) -> Optional[str]: """Encode data for xml rendering.""" if data is None or isinstance(data, str): return data if isinstance(data, list) and not data: return None return converter.serialize(data, ns_map=self.ns_map)
def prepare_generic_value(cls, qname: str, value: Any) -> Any: """Prepare parsed value before binding to a wildcard field.""" if not is_dataclass(value): value = AnyElement(qname=qname, text=converter.serialize(value)) elif not isinstance(value, (AnyElement, DerivedElement)): value = DerivedElement(qname=qname, value=value) return value
def test_serialize(self): with warnings.catch_warnings(record=True): self.assertEqual(None, converter.deserialize(None, [int])) self.assertEqual("1", converter.serialize(1)) self.assertEqual("1 2 3", converter.serialize([1, "2", 3])) self.assertEqual(None, converter.serialize(None)) self.assertEqual("1", converter.serialize(1)) self.assertEqual("1.5", converter.serialize(1.5)) self.assertEqual("true", converter.serialize(True)) self.assertEqual("optional", converter.serialize(UseType.OPTIONAL)) self.assertEqual("8.77683E-8", converter.serialize(Decimal("8.77683E-8"))) self.assertEqual("8.77683E-08", converter.serialize(float("8.77683E-8")))
def convert(self, obj: Any, var: Optional[XmlVar] = None) -> Any: if var is None or is_dataclass(obj): return self.dict_factory([ (var.lname, self.convert(getattr(obj, var.name), var)) for var in self.context.build(obj.__class__).vars ]) if isinstance(obj, (list, tuple)): return type(obj)(self.convert(v, var) for v in obj) if isinstance(obj, (dict, int, float, str, bool)): return obj if isinstance(obj, Enum): return self.convert(obj.value, var) return converter.serialize(obj, format=var.format)
def test_register_converter(self): class MinusOneInt(int): pass class MinusOneIntConverter(Converter): def deserialize(self, value: str, **kwargs: Any) -> Any: return int(value) - 1 def serialize(self, value: Any, **kwargs: Any) -> str: return str(value) self.assertEqual(1, converter.deserialize("1", [MinusOneInt])) converter.register_converter(MinusOneInt, MinusOneIntConverter()) self.assertEqual(1, converter.deserialize("2", [MinusOneInt])) self.assertEqual(2, converter.deserialize("3", [MinusOneInt])) self.assertEqual("3", converter.serialize(MinusOneInt("3"))) converter.unregister_converter(MinusOneInt)
def test_register_converter(self): class MinusOneInt(int): pass class MinusOneIntConverter(Converter): def deserialize(self, value: str, **kwargs: Any) -> Any: return int(value) - 1 def serialize(self, value: Any, **kwargs: Any) -> str: return str(value) with warnings.catch_warnings(record=True) as w: self.assertEqual("1", converter.deserialize("1", [MinusOneInt])) self.assertEqual(f"No converter registered for `{MinusOneInt}`", str(w[-1].message)) converter.register_converter(MinusOneInt, MinusOneIntConverter()) self.assertEqual(1, converter.deserialize("2", [MinusOneInt])) self.assertEqual(2, converter.deserialize("3", [MinusOneInt])) self.assertEqual("3", converter.serialize(MinusOneInt("3"))) converter.unregister_converter(MinusOneInt)
def set_data(self, data: Any): """ Set data notification receiver. The receiver will convert the data to string, flush any previous pending start element and send it to the handler for generation. If the text content of the tag has already been generated then treat the current data as element tail content and queue it to be generated when the tag ends. :param data: Element text or tail content """ value = converter.serialize(data, ns_map=self.ns_map) self.flush_start(is_nil=value is None or value == "") if value: if not self.in_tail: self.handler.characters(value) else: self.tail = value self.in_tail = True
def add_attribute(self, key: str, value: Any, check_pending: bool = True): """ Add attribute notification receiver. The receiver will convert the key to a namespace, name tuple and convert the value to string. Internally the converter will also generate any missing namespace prefixes. :param key: Attribute name :param value: Attribute value :param check_pending: Raise exception if not no element is pending start """ if not self.pending_tag and check_pending: raise XmlWriterError("Empty pending tag.") if self.is_xsi_type(key, value): value = QName(value) name = split_qname(key) value = converter.serialize(value, ns_map=self.ns_map) self.attrs[name] = value
def encode_value(cls, value: Any, var: XmlVar) -> Any: """ Encode values for xml serialization. Converts values to strings. QName instances is an exception, those values need to wait until the XmlWriter assigns prefixes to namespaces per element node. Enums and Tokens may contain QName(s) so they also get a special treatment. We can't do all the conversions in the writer because we would need to carry the xml vars inside the writer. Instead of that we do the easy encoding here and leave the qualified names for later. """ if isinstance(value, (str, QName)) or var is None: return value if isinstance(value, (tuple, list)): return [cls.encode_value(v, var) for v in value] if isinstance(value, Enum): return cls.encode_value(value.value, var) return converter.serialize(value, format=var.format)