diff --git a/tests/test_converters.py b/tests/test_converters.py index ddf77002..480029d2 100644 --- a/tests/test_converters.py +++ b/tests/test_converters.py @@ -9,9 +9,9 @@ # @author Davide Brunato # import unittest -from xml.etree.ElementTree import Element, parse as etree_parse from pathlib import Path from typing import cast, MutableMapping, Optional, Type +from xml.etree.ElementTree import Element, parse as etree_parse try: import lxml.etree as lxml_etree @@ -26,7 +26,7 @@ from xmlschema.converters import XMLSchemaConverter, UnorderedConverter, \ ParkerConverter, BadgerFishConverter, AbderaConverter, JsonMLConverter, \ - ColumnarConverter + ColumnarConverter, GData from xmlschema.dataobjects import DataElementConverter @@ -439,6 +439,34 @@ def test_decode_encode_badgerfish_converter(self): root = col_schema.encode(obj2) # No namespace unmap is required self.assertIsNone(etree_elements_assert_equal(self.col_xml_root, root, strict=False)) + def test_decode_encode_gdata_converter(self): + col_schema = XMLSchema(self.col_xsd_filename, converter=GData) + + obj1 = col_schema.decode(self.col_xml_filename) + self.assertIn("'@xmlns'", repr(obj1)) + + root = col_schema.encode(obj1, path='./col:collection', namespaces=self.col_nsmap) + self.assertIsNone(etree_elements_assert_equal(self.col_xml_root, root, strict=False)) + + root = col_schema.encode(obj1) + self.assertIsNone(etree_elements_assert_equal(self.col_xml_root, root, strict=False)) + + # With ElementTree namespaces are not mapped + obj2 = col_schema.decode(self.col_xml_root) + self.assertNotIn("'@xmlns'", repr(obj2)) + self.assertNotEqual(obj1, obj2) + self.assertEqual(obj1, col_schema.decode(self.col_xml_root, namespaces=self.col_nsmap)) + + # With lxml.etree namespaces are mapped + if self.col_lxml_root is not None: + self.assertEqual(obj1, col_schema.decode(self.col_lxml_root)) + + root = col_schema.encode(obj2, path='./col:collection', namespaces=self.col_nsmap) + self.assertIsNone(etree_elements_assert_equal(self.col_xml_root, root, strict=False)) + + root = col_schema.encode(obj2) # No namespace unmap is required + self.assertIsNone(etree_elements_assert_equal(self.col_xml_root, root, strict=False)) + def test_decode_encode_abdera_converter(self): col_schema = XMLSchema(self.col_xsd_filename, converter=AbderaConverter) @@ -675,6 +703,7 @@ def test_mixed_content__issue_315(self): if __name__ == '__main__': import platform + header_template = "Test xmlschema converters with Python {} on {}" header = header_template.format(platform.python_version(), platform.platform()) print('{0}\n{1}\n{0}'.format("*" * len(header), header)) diff --git a/xmlschema/__init__.py b/xmlschema/__init__.py index f8f947a9..b1221e8c 100644 --- a/xmlschema/__init__.py +++ b/xmlschema/__init__.py @@ -18,7 +18,7 @@ from .xpath import ElementPathMixin from .converters import ElementData, XMLSchemaConverter, \ UnorderedConverter, ParkerConverter, BadgerFishConverter, \ - AbderaConverter, JsonMLConverter, ColumnarConverter + AbderaConverter, JsonMLConverter, ColumnarConverter, GData from .dataobjects import DataElement, DataElementConverter, DataBindingConverter from .documents import validate, is_valid, iter_errors, iter_decode, \ to_dict, to_json, to_etree, from_json, XmlDocument @@ -44,7 +44,7 @@ 'XMLSchemaNamespaceError', 'etree_tostring', 'normalize_url', 'normalize_locations', 'fetch_resource', 'fetch_namespaces', 'fetch_schema_locations', 'fetch_schema', 'XMLResource', 'ElementPathMixin', 'ElementData', 'XMLSchemaConverter', - 'UnorderedConverter', 'ParkerConverter', 'BadgerFishConverter', 'AbderaConverter', + 'UnorderedConverter', 'ParkerConverter', 'BadgerFishConverter', 'GData', 'AbderaConverter', 'JsonMLConverter', 'ColumnarConverter', 'DataElement', 'DataElementConverter', 'DataBindingConverter', 'validate', 'is_valid', 'iter_errors', 'iter_decode', 'to_dict', 'to_json', 'to_etree', 'from_json', 'XmlDocument', 'XMLSchemaValidatorError', diff --git a/xmlschema/cli.py b/xmlschema/cli.py index d77d3452..93beb437 100644 --- a/xmlschema/cli.py +++ b/xmlschema/cli.py @@ -26,6 +26,7 @@ 'unordered': xmlschema.UnorderedConverter, 'parker': xmlschema.ParkerConverter, 'badgerfish': xmlschema.BadgerFishConverter, + 'gdata': xmlschema.GData, 'abdera': xmlschema.AbderaConverter, 'jsonml': xmlschema.JsonMLConverter, 'columnar': xmlschema.ColumnarConverter, diff --git a/xmlschema/converters/__init__.py b/xmlschema/converters/__init__.py index 3dac3ed1..f2340a03 100644 --- a/xmlschema/converters/__init__.py +++ b/xmlschema/converters/__init__.py @@ -11,10 +11,11 @@ from .unordered import UnorderedConverter from .parker import ParkerConverter from .badgerfish import BadgerFishConverter +from .gdata import GData from .abdera import AbderaConverter from .jsonml import JsonMLConverter from .columnar import ColumnarConverter __all__ = ['XMLSchemaConverter', 'UnorderedConverter', 'ParkerConverter', 'BadgerFishConverter', 'AbderaConverter', 'JsonMLConverter', - 'ColumnarConverter', 'ElementData'] + 'ColumnarConverter', 'ElementData', 'GData'] diff --git a/xmlschema/converters/gdata.py b/xmlschema/converters/gdata.py new file mode 100644 index 00000000..dd9fbf2b --- /dev/null +++ b/xmlschema/converters/gdata.py @@ -0,0 +1,39 @@ +# +# Copyright (c), 2016-2021, SISSA (International School for Advanced Studies). +# All rights reserved. +# This file is distributed under the terms of the MIT License. +# See the file 'LICENSE' in the root directory of the present +# distribution, or http://opensource.org/licenses/MIT. +# +# @author Mikhail Razgovorov <1338833@gmail.com> +# +from typing import TYPE_CHECKING, Any, Optional, List, Dict, Type + +from .badgerfish import BadgerFishConverter +from ..aliases import NamespacesType + +if TYPE_CHECKING: + pass + + +class GData(BadgerFishConverter): + """ + XML Schema based converter class for Badgerfish convention. + + ref: http://www.sklar.com/badgerfish/ + ref: http://badgerfish.ning.com/ + + :param namespaces: Map from namespace prefixes to URI. + :param dict_class: Dictionary class to use for decoded data. Default is `dict`. + :param list_class: List class to use for decoded data. Default is `list`. + """ + __slots__ = () + + def __init__(self, namespaces: Optional[NamespacesType] = None, + dict_class: Optional[Type[Dict[str, Any]]] = None, + list_class: Optional[Type[List[Any]]] = None, + **kwargs: Any) -> None: + kwargs.update(attr_prefix='', text_key='$t', cdata_prefix='$') + super(BadgerFishConverter, self).__init__( + namespaces, dict_class, list_class, **kwargs + ) diff --git a/xmlschema/testing/_builders.py b/xmlschema/testing/_builders.py index da6988e9..1064ef89 100644 --- a/xmlschema/testing/_builders.py +++ b/xmlschema/testing/_builders.py @@ -33,7 +33,7 @@ import xmlschema from xmlschema import XMLSchemaBase, XMLSchema11, XMLSchemaValidationError, \ XMLSchemaParseError, UnorderedConverter, ParkerConverter, BadgerFishConverter, \ - AbderaConverter, JsonMLConverter, ColumnarConverter + AbderaConverter, JsonMLConverter, ColumnarConverter, GData from xmlschema.names import XSD_IMPORT from xmlschema.helpers import local_name from xmlschema.resources import fetch_namespaces @@ -466,6 +466,7 @@ def check_data_conversion_with_element_tree(self): self.check_decode_encode(root, ParkerConverter, validation='lax', **options) self.check_decode_encode(root, ParkerConverter, validation='skip', **options) self.check_decode_encode(root, BadgerFishConverter, **options) + self.check_decode_encode(root, GData, **options) self.check_decode_encode(root, AbderaConverter, **options) # self.check_decode_encode(root, JsonMLConverter, **options) self.check_decode_encode(root, ColumnarConverter, validation='lax', **options) @@ -479,6 +480,7 @@ def check_data_conversion_with_element_tree(self): self.check_json_serialization(root, ParkerConverter, validation='lax', **options) self.check_json_serialization(root, ParkerConverter, validation='skip', **options) self.check_json_serialization(root, BadgerFishConverter, **options) + self.check_json_serialization(root, GData, **options) self.check_json_serialization(root, AbderaConverter, **options) # self.check_json_serialization(root, JsonMLConverter, **options) self.check_json_serialization(root, ColumnarConverter, validation='lax', **options) @@ -521,6 +523,7 @@ def check_data_conversion_with_lxml(self): self.check_decode_encode(root, cdata_prefix='#', **options) # Default converter self.check_decode_encode(root, UnorderedConverter, cdata_prefix='#', **options) self.check_decode_encode(root, BadgerFishConverter, **options) + self.check_decode_encode(root, GData, **options) self.check_decode_encode(root, JsonMLConverter, **options) # Tests with converters that loss namespace information and JSON @@ -545,6 +548,7 @@ def check_data_conversion_with_lxml(self): self.check_json_serialization(root, ParkerConverter, validation='lax', **options) self.check_json_serialization(root, ParkerConverter, validation='skip', **options) self.check_json_serialization(root, BadgerFishConverter, **options) + self.check_json_serialization(root, GData, **options) self.check_json_serialization(root, AbderaConverter, **options) # self.check_json_serialization(root, JsonMLConverter, **options)