def test_find_var(self): ctx = XmlContext() meta = ctx.build(BookForm) self.assertTrue(meta.find_var("author").element) self.assertIsNone(meta.find_var("author", FindMode.ATTRIBUTE)) self.assertIsNone(meta.find_var("nope"))
def test_bind_returns_best_matching_dataclass(self): @dataclass class Item: value: str = field() a: int = attribute() b: int = attribute() @dataclass class Item2: a: int = attribute() @dataclass class Root: item: Union[str, int, Item2, Item] = element() ctx = XmlContext() meta = ctx.build(Root) var = meta.vars[0] attrs = {"a": "1", "b": 2} ns_map = {} node = UnionNode(position=0, var=var, context=ctx, attrs=attrs, ns_map=ns_map) objects = [] self.assertTrue(node.bind("item", "foo", None, objects)) self.assertIsInstance(objects[-1][1], Item) self.assertEqual(1, objects[-1][1].a) self.assertEqual(2, objects[-1][1].b) self.assertEqual("foo", objects[-1][1].value) self.assertEqual("item", objects[-1][0]) self.assertEqual(2, len(node.events)) self.assertEqual(("start", "item", attrs, ns_map), node.events[0]) self.assertEqual(("end", "item", "foo", None), node.events[1]) self.assertIsNot(node.attrs, node.events[0][2]) self.assertIs(node.ns_map, node.events[0][3])
def test_bind_objects(self): @dataclass class A: x: int y: int = field(init=False) w: object = field(metadata=dict(type=XmlType.WILDCARD)) ctx = XmlContext() meta = ctx.build(A) x = meta.find_var("x") y = meta.find_var("y") w = meta.find_var("wild") wild_element = AnyElement(qname="foo") objects = [ ("foo", 0), (x.qname, 1), (y.qname, 2), (w.qname, None), (w.qname, wild_element), ] params = {} ParserUtils.bind_objects(params, meta, 1, objects) expected = { "x": 1, "w": AnyElement( children=[AnyElement(qname="w", text=""), AnyElement(qname="foo")] ), } self.assertEqual(expected, params)
def test_next_node_return_self_on_root_element(self): ele = Element("foo") ctx = XmlContext() cfg = ParserConfig() meta = ctx.build(Foo) node = RootNode(position=0, meta=meta, config=cfg) self.assertIs(node, node.next_node(ele, 0, ctx))
def test_find_var(self): ctx = XmlContext() meta = ctx.build(BookForm) author = QName("author") excluded = set() excluded.add("author") self.assertIsInstance(meta.find_var(author), XmlElement) self.assertIsNone(meta.find_var(author, FindMode.ATTRIBUTE)) self.assertIsNone(meta.find_var(QName("nope")))
def test_find_var_uses_cache(self): ctx = XmlContext() meta = ctx.build(BookForm) self.assertEqual("author", meta.find_var("author").name) self.assertEqual(1, len(meta.cache)) key = tuple(meta.cache.keys())[0] meta.cache[key] = 1 self.assertEqual("title", meta.find_var("author").name)
def setUp(self) -> None: super().setUp() self.context = XmlContext() self.meta = XmlMeta(clazz=Foo, qname="foo", source_qname="foo", nillable=False) self.node = ElementNode( position=0, meta=self.meta, context=self.context, config=ParserConfig(), attrs={}, ns_map={}, )
def test_next_node_return_next_node(self): root = Element("a") ele = SubElement(root, "b") ctx = XmlContext() cfg = ParserConfig() meta = ctx.build(Foo) node = RootNode(position=0, meta=meta, config=cfg) actual = node.next_node(ele, 0, ctx) self.assertIsInstance(actual, PrimitiveNode) self.assertIsNot(actual, node)
def next_node(self, element: Element, position: int, ctx: XmlContext) -> XmlNode: qname = QName(element.tag) var = self.meta.find_var(qname, FindMode.NOT_WILDCARD) if not var: var = self.meta.find_var(qname, FindMode.WILDCARD) if not var: if self.config.fail_on_unknown_properties: raise XmlContextError( f"{self.meta.qname} does not support mixed content: {qname}" ) return SkipNode(position=position) if var.clazz: xsi_type = ParserUtils.parse_xsi_type(element) meta = ctx.fetch(var.clazz, self.meta.qname.namespace, xsi_type) return ElementNode(position=position, meta=meta, default=var.default, config=self.config) if var.is_any_type: return WildcardNode(position=position, qname=var.qname) return PrimitiveNode( position=position, types=var.types, default=var.default, tokens=var.is_tokens, )
def next_node(self, element: Element, position: int, ctx: XmlContext) -> XmlNode: """ Initialize the next node to be queued for the given starting element. Search by the given element tag for a matching variable and create the next node by the variable type. :return: The next node to be queued. :raises: XmlContextError if the element is unknown and parser config is strict. """ qname = QName(element.tag) var = self.meta.find_var(qname, FindMode.NOT_WILDCARD) if not var: var = self.meta.find_var(qname, FindMode.WILDCARD) if not var: if self.config.fail_on_unknown_properties: raise XmlContextError( f"{self.meta.qname} does not support mixed content: {qname}" ) return SkipNode(position=position) if var.clazz: xsi_type = ParserUtils.parse_xsi_type(element) meta = ctx.fetch(var.clazz, self.meta.qname.namespace, xsi_type) return ElementNode(position=position, meta=meta, config=self.config) if var.is_any_type: return WildcardNode(position=position, qname=var.qname) return PrimitiveNode(position=position, var=var)
def test_next_node_when_given_qname_matches_dataclass_var( self, mock_ctx_fetch, mock_element_xsi_type): ele = Element("a") ctx = XmlContext() cfg = ParserConfig() var = XmlElement(name="a", qname=QName("a"), types=[Foo], dataclass=True) meta = XmlMeta( name="foo", clazz=None, qname=QName("foo"), source_qname=QName("foo"), nillable=False, vars=[var], ) xsi_type = QName("foo") namespace = meta.qname.namespace mock_ctx_fetch.return_value = replace(meta) mock_element_xsi_type.return_value = xsi_type node = ElementNode(position=0, meta=meta, config=cfg) actual = node.next_node(ele, 10, ctx) self.assertIsInstance(actual, ElementNode) self.assertEqual(10, actual.position) self.assertIs(mock_ctx_fetch.return_value, actual.meta) mock_ctx_fetch.assert_called_once_with(var.clazz, namespace, xsi_type)
def test_next_node(self): ele = Element("foo") node = PrimitiveNode(position=0, var=XmlText(name="foo", qname=QName("foo"))) with self.assertRaises(XmlContextError): node.next_node(ele, 10, XmlContext())
def validate_bindings(schema: Path, clazz: Type, output_format: str): __tracebackhide__ = True chapter = schema.stem.replace("chapter", "") sample = here.joinpath(f"samples/chapter{chapter}.xml") output_xml = here.joinpath(f"output/chapter{chapter}.xsdata.xml") output_json = here.joinpath(f"output/chapter{chapter}.xsdata.json") context = XmlContext(class_type=output_format) obj = XmlParser(context=context).from_path(sample, clazz) config = SerializerConfig(pretty_print=True) actual_json = JsonSerializer(context=context, config=config).render(obj) actual_xml = XmlSerializer(context=context, config=config).render(obj) if output_json.exists() and chapter != "13": assert output_json.read_text() == actual_json assert obj == JsonParser(context=context).from_string( actual_json, clazz) else: output_json.write_text(actual_json, encoding="utf-8") if output_xml.exists(): assert output_xml.read_text() == actual_xml else: output_xml.write_text(actual_xml, encoding="utf-8") validator = etree.XMLSchema(etree.parse(str(schema))) validator.assertValid(etree.fromstring(actual_xml.encode()))
def test_bind_elements( self, mock_bind_element_param, mock_find_eligible_wildcard, mock_bind_element_wildcard_param, ): @dataclass class A: x: int y: int = field(init=False) w: object = field(metadata=dict(type=XmlType.WILDCARD)) ctx = XmlContext() meta = ctx.build(A) x = meta.find_var(QName("x")) y = meta.find_var(QName("y")) w = meta.find_var(QName("wild")) wild_element = AnyElement(qname="foo") objects = [ ("foo", 0), (x.qname, 1), (y.qname, 2), (w.qname, None), (w.qname, wild_element), ] mock_bind_element_param.side_effect = [True, False, False] mock_find_eligible_wildcard.side_effect = [None, w] params = {} ParserUtils.bind_element_children(params, meta, 1, objects) mock_bind_element_param.assert_has_calls( [ mock.call(params, x, 1), mock.call(params, w, ""), mock.call(params, w, wild_element), ] ) mock_find_eligible_wildcard.assert_has_calls( [mock.call(meta, w.qname, params), mock.call(meta, QName("foo"), params)] ) mock_bind_element_wildcard_param.assert_called_once_with( params, w, w.qname, wild_element )
def test_next_node(self): ele = Element("foo") node = WildcardNode(position=0, qname="a") actual = node.next_node(ele, 10, XmlContext()) self.assertIsInstance(actual, WildcardNode) self.assertEqual(10, actual.position) self.assertEqual("a", actual.qname)
def read(cls, path: Path) -> "GeneratorConfig": ctx = XmlContext( element_name_generator=text.pascal_case, attribute_name_generator=text.camel_case, ) config = ParserConfig(fail_on_unknown_properties=False) parser = XmlParser(context=ctx, config=config) return parser.from_path(path, cls)
def test_parse_element( self, mock_bind_element_attrs, mock_bind_element_text, mock_bind_element_children, mock_bind_element_wild_text, ): def add_attr(x, *args): x["a"] = 1 def add_text(x, *args): x["b"] = 2 def add_child(x, *args): x["c"] = 3 def add_wild_text(x, *args): x["d"] = 4 mock_bind_element_attrs.side_effect = add_attr mock_bind_element_text.side_effect = add_text mock_bind_element_children.side_effect = add_child mock_bind_element_wild_text.side_effect = add_wild_text ctx = XmlContext() meta = ctx.build(Foo) ele = Element("foo") pool = [1, 2, 3] node = ElementNode(position=0, meta=meta, default=None, config=ParserConfig()) qname, obj = node.parse_element(ele, pool) self.assertEqual(QName(ele.tag), qname) self.assertEqual(Foo(1, 2, 3, 4), obj) mock_bind_element_attrs.assert_called_once_with(mock.ANY, meta, ele) mock_bind_element_text.assert_called_once_with(mock.ANY, meta, ele) mock_bind_element_children.assert_called_once_with( mock.ANY, meta, 0, pool) mock_bind_element_wild_text.assert_called_once_with( mock.ANY, meta, ele)
def __init__(self, *, config: Configuration) -> None: self.config = config self.serializer = XmlSerializer(config=SerializerConfig( pretty_print=False)) self.parser = XmlParser(context=XmlContext()) self.requests: WeakValueDictionary[str, Future] = WeakValueDictionary({}) super().__init__()
def test_bind_raises_parser_error_on_failure(self): @dataclass class Item: value: str = field() @dataclass class Root: item: Union[int, Item] = element() ctx = XmlContext() meta = ctx.build(Root) meta.vars[0] node = UnionNode(position=0, var=meta.vars[0], context=ctx, attrs={}, ns_map={}) with self.assertRaises(ParserError) as cm: node.bind("item", None, None, []) self.assertEqual("Failed to parse union node: item", str(cm.exception))
def test_bind_objects_with_unassigned_object(self, mock_warning): a = make_dataclass("a", [("x", int)]) meta = XmlContext().build(a) params = {} objects = [("x", 1), ("y", 2)] ParserUtils.bind_objects(params, meta, 0, objects) self.assertEqual({"x": 1}, params) mock_warning.assert_called_once_with("Unassigned parsed object %s", "y")
def serialize_xml_from_file(xml_file_path: pathlib.Path, serialize_clazz: Optional[Type[T]]): """ Method to serialize XML data from a file. :param xml_file_path: A pathlib.path path object that leads to the targeted XML file. :param serialize_clazz: A class Object. :return: the serialized object. """ parser = XmlParser(context=XmlContext()) return parser.from_path(xml_file_path, serialize_clazz)
def test_bind_appends_end_event_when_level_not_zero(self): ctx = XmlContext() var = XmlVar(text=True, name="foo", qname="foo") node = UnionNode(position=0, var=var, context=ctx, attrs={}, ns_map={}) node.level = 1 objects = [] self.assertFalse(node.bind("bar", "text", "tail", objects)) self.assertEqual(0, len(objects)) self.assertEqual(0, node.level) self.assertEqual([("end", "bar", "text", "tail")], node.events)
def test_child(self): attrs = {"id": "1"} ns_map = {"ns0": "xsdata"} ctx = XmlContext() var = XmlVar(text=True, name="foo", qname="foo") node = UnionNode(position=0, var=var, context=ctx, attrs={}, ns_map={}) self.assertEqual(node, node.child("foo", attrs, ns_map, 10)) self.assertEqual(1, node.level) self.assertEqual([("start", "foo", attrs, ns_map)], node.events) self.assertIsNot(attrs, node.events[0][2])
def assert_bindings( schema: str, instance: str, class_name: str, version: str, mode: str, save_output: bool, output_format: str, structure_style: str, ): __tracebackhide__ = True if mode == "xml": instance_path = Path(instance) pck_arr = list(map(text.snake_case, instance_path.parts)) package = f"output.xml_models.{'.'.join(pck_arr)}" instance_path = w3c.joinpath(instance_path) source = str(instance_path) else: schema_path = Path(schema) pck_arr = list(map(text.snake_case, schema_path.parts)) package = f"output.models.{'.'.join(pck_arr)}" schema_path = w3c.joinpath(schema) source = str(schema_path) clazz = generate_models(source, package, class_name, output_format, structure_style) if mode == "build": return if isinstance(clazz, Exception): raise clazz try: instance_path = w3c.joinpath(instance) schema_path = w3c.joinpath(schema) context = XmlContext(class_type=output_format) parser = XmlParser(context=context) obj = parser.from_path(instance_path, clazz) except Exception as e: raise e save_path = None if save_output: save_path = output.joinpath(instance) save_path.parent.mkdir(parents=True, exist_ok=True) if mode == "json": assert_json_bindings(context, obj, save_path) else: assert_xml_bindings( context, obj, parser.ns_map, schema_path, instance_path, save_path, version )
def write(cls, output: TextIO, obj: "GeneratorConfig"): ctx = XmlContext( element_name_generator=text.pascal_case, attribute_name_generator=text.camel_case, ) config = SerializerConfig(pretty_print=True) serializer = XmlSerializer(context=ctx, config=config, writer=XmlEventWriter) serializer.write(output, obj, ns_map={None: "http://pypi.org/project/xsdata"})
def test_next_node_when_config_fail_on_unknown_properties_is_false(self): ele = Element("nope") ctx = XmlContext() cfg = ParserConfig(fail_on_unknown_properties=False) meta = XmlMeta( name="foo", clazz=None, qname=QName("foo"), source_qname=QName("foo"), nillable=False, ) node = ElementNode(position=0, meta=meta, config=cfg) actual = node.next_node(ele, 10, ctx) self.assertEqual(SkipNode(position=10), actual)
def test_next_node_when_given_qname_does_not_match_any_var(self): ele = Element("nope") ctx = XmlContext() cfg = ParserConfig() meta = XmlMeta( name="foo", clazz=None, qname=QName("foo"), source_qname=QName("foo"), nillable=False, ) node = ElementNode(position=0, meta=meta, config=cfg) with self.assertRaises(XmlContextError) as cm: node.next_node(ele, 10, ctx) self.assertEqual("foo does not support mixed content: nope", str(cm.exception))
def test_next_node_when_given_qname_matches_primitive_var(self): ele = Element("a") ctx = XmlContext() cfg = ParserConfig() var = XmlText(name="a", qname=QName("a"), types=[int], default=100) meta = XmlMeta( name="foo", clazz=None, qname=QName("foo"), source_qname=QName("foo"), nillable=False, vars=[var], ) node = ElementNode(position=0, meta=meta, config=cfg) actual = node.next_node(ele, 10, ctx) self.assertIsInstance(actual, PrimitiveNode) self.assertEqual(10, actual.position) self.assertEqual(var, actual.var)
def deserialize(resource: Union[str, Path, bytes], target_class: Optional[Type[T]] = None) -> Optional[T]: parser = XmlParser(context=XmlContext()) obj = None if os.path.isfile(resource): resource = Path(resource) if isinstance(resource, str): obj = parser.from_string(resource, target_class) if isinstance(resource, Path): obj = parser.from_path(resource, target_class) if isinstance(resource, bytes): obj = parser.from_bytes(resource, target_class) if obj and hasattr(obj, 'complemento') and obj.complemento: complementos = __deserialize_complementos(obj) setattr(obj.complemento, 'any_element', complementos) return obj
def test_next_node_when_given_qname_matches_any_element_var(self): ele = Element("a") ctx = XmlContext() cfg = ParserConfig() var = XmlWildcard(name="a", qname=QName("a"), types=[], dataclass=False) meta = XmlMeta( name="foo", clazz=None, qname=QName("foo"), source_qname=QName("foo"), nillable=False, vars=[var], ) node = ElementNode(position=0, meta=meta, config=cfg) actual = node.next_node(ele, 10, ctx) self.assertIsInstance(actual, WildcardNode) self.assertEqual(10, actual.position) self.assertEqual(var.qname, actual.qname)