1# -*- coding: utf-8 -*-
2#--------------------------------------------------------------------------
3#
4# Copyright (c) Microsoft Corporation. All rights reserved.
5#
6# The MIT License (MIT)
7#
8# Permission is hereby granted, free of charge, to any person obtaining a copy
9# of this software and associated documentation files (the ""Software""), to deal
10# in the Software without restriction, including without limitation the rights
11# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12# copies of the Software, and to permit persons to whom the Software is
13# furnished to do so, subject to the following conditions:
14#
15# The above copyright notice and this permission notice shall be included in
16# all copies or substantial portions of the Software.
17#
18# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24# THE SOFTWARE.
25#
26#--------------------------------------------------------------------------
27import sys
28import xml.etree.ElementTree as ET
29
30import pytest
31
32from msrest.serialization import Serializer, Deserializer, Model, xml_key_extractor
33
34
35def assert_xml_equals(x1, x2):
36    print("--------X1--------")
37    ET.dump(x1)
38    print("--------X2--------")
39    ET.dump(x2)
40
41    assert x1.tag == x2.tag
42    assert (x1.text or "").strip() == (x2.text or "").strip()
43    # assert x1.tail == x2.tail # Swagger does not change tail
44    assert x1.attrib == x2.attrib
45    assert len(x1) == len(x2)
46    for c1, c2 in zip(x1, x2):
47        assert_xml_equals(c1, c2)
48
49class TestXmlDeserialization:
50
51    def test_basic(self):
52        """Test an ultra basic XML."""
53        basic_xml = """<?xml version="1.0"?>
54            <Data country="france">
55                <Long>12</Long>
56                <EmptyLong/>
57                <Age>37</Age>
58                <EmptyAge/>
59                <EmptyString/>
60            </Data>"""
61
62        class XmlModel(Model):
63            _attribute_map = {
64                'longnode': {'key': 'longnode', 'type': 'long', 'xml':{'name': 'Long'}},
65                'empty_long': {'key': 'empty_long', 'type': 'long', 'xml':{'name': 'EmptyLong'}},
66                'age': {'key': 'age', 'type': 'int', 'xml':{'name': 'Age'}},
67                'empty_age': {'key': 'empty_age', 'type': 'int', 'xml':{'name': 'EmptyAge'}},
68                'empty_string': {'key': 'empty_string', 'type': 'str', 'xml':{'name': 'EmptyString'}},
69                'not_set': {'key': 'not_set', 'type': 'str', 'xml':{'name': 'NotSet'}},
70                'country': {'key': 'country', 'type': 'str', 'xml':{'name': 'country', 'attr': True}},
71            }
72            _xml_map = {
73                'name': 'Data'
74            }
75
76        s = Deserializer({"XmlModel": XmlModel})
77        result = s(XmlModel, basic_xml, "application/xml")
78
79        assert result.longnode == 12
80        assert result.empty_long is None
81        assert result.age == 37
82        assert result.empty_age is None
83        assert result.country == "france"
84        assert result.empty_string == ""
85        assert result.not_set is None
86
87    def test_basic_unicode(self):
88        """Test a XML with unicode."""
89        basic_xml = u"""<?xml version="1.0" encoding="utf-8"?>
90            <Data language="français"/>"""
91
92        class XmlModel(Model):
93            _attribute_map = {
94                'language': {'key': 'language', 'type': 'str', 'xml':{'name': 'language', 'attr': True}},
95            }
96            _xml_map = {
97                'name': 'Data'
98            }
99
100        s = Deserializer({"XmlModel": XmlModel})
101        result = s(XmlModel, basic_xml, "application/xml")
102
103        assert result.language == u"français"
104
105    def test_basic_text(self):
106        """Test a XML with unicode."""
107        basic_xml = u"""<?xml version="1.0" encoding="utf-8"?>
108            <Data language="english">I am text</Data>"""
109
110        class XmlModel(Model):
111            _attribute_map = {
112                'language': {'key': 'language', 'type': 'str', 'xml':{'name': 'language', 'attr': True}},
113                'content': {'key': 'content', 'type': 'str', 'xml':{'text': True}},
114            }
115            _xml_map = {
116                'name': 'Data'
117            }
118
119        s = Deserializer({"XmlModel": XmlModel})
120        result = s(XmlModel, basic_xml, "application/xml")
121
122        assert result.language == "english"
123        assert result.content == "I am text"
124
125    def test_add_prop(self):
126        """Test addProp as a dict.
127        """
128        basic_xml = """<?xml version="1.0"?>
129            <Data>
130                <Metadata>
131                  <Key1>value1</Key1>
132                  <Key2>value2</Key2>
133                </Metadata>
134            </Data>"""
135
136        class XmlModel(Model):
137            _attribute_map = {
138                'metadata': {'key': 'Metadata', 'type': '{str}', 'xml': {'name': 'Metadata'}},
139            }
140            _xml_map = {
141                'name': 'Data'
142            }
143
144        s = Deserializer({"XmlModel": XmlModel})
145        result = s(XmlModel, basic_xml, "application/xml")
146
147        assert len(result.metadata) == 2
148        assert result.metadata['Key1'] == "value1"
149        assert result.metadata['Key2'] == "value2"
150
151    def test_object(self):
152        basic_xml = """<?xml version="1.0"?>
153            <Data country="france">
154                <Age>37</Age>
155            </Data>"""
156
157        s = Deserializer()
158        result = s('object', basic_xml, "application/xml")
159
160        # Should be a XML tree
161        assert result.tag == "Data"
162        assert result.get("country") == "france"
163        for child in result:
164            assert child.tag == "Age"
165            assert child.text == "37"
166
167    def test_object_no_text(self):
168        basic_xml = """<?xml version="1.0"?><Data country="france"><Age>37</Age></Data>"""
169
170        s = Deserializer()
171        result = s('object', basic_xml, "application/xml")
172
173        # Should be a XML tree
174        assert result.tag == "Data"
175        assert result.get("country") == "france"
176        for child in result:
177            assert child.tag == "Age"
178            assert child.text == "37"
179
180    def test_basic_empty(self):
181        """Test an basic XML with an empty node."""
182        basic_xml = """<?xml version="1.0"?>
183            <Data>
184                <Age/>
185            </Data>"""
186
187        class XmlModel(Model):
188            _attribute_map = {
189                'age': {'key': 'age', 'type': 'str', 'xml':{'name': 'Age'}},
190            }
191            _xml_map = {
192                'name': 'Data'
193            }
194
195        s = Deserializer({"XmlModel": XmlModel})
196        result = s(XmlModel, basic_xml, "application/xml")
197
198        assert result.age == ""
199
200    def test_basic_empty_list(self):
201        """Test an basic XML with an empty node."""
202        basic_xml = """<?xml version="1.0"?>
203            <Data/>"""
204
205        class XmlModel(Model):
206            _attribute_map = {
207                'age': {'key': 'age', 'type': 'str', 'xml':{'name': 'Age'}},
208            }
209            _xml_map = {
210                'name': 'Data'
211            }
212
213        s = Deserializer({"XmlModel": XmlModel})
214        result = s('[XmlModel]', basic_xml, "application/xml")
215
216        assert result == []
217
218    def test_list_wrapped_items_name_basic_types(self):
219        """Test XML list and wrap, items is basic type and there is itemsName.
220        """
221
222        basic_xml = """<?xml version="1.0"?>
223            <AppleBarrel>
224                <GoodApples>
225                  <Apple>granny</Apple>
226                  <Apple>fuji</Apple>
227                </GoodApples>
228            </AppleBarrel>"""
229
230        class AppleBarrel(Model):
231            _attribute_map = {
232                'good_apples': {'key': 'GoodApples', 'type': '[str]', 'xml': {'name': 'GoodApples', 'wrapped': True, 'itemsName': 'Apple'}},
233            }
234            _xml_map = {
235                'name': 'AppleBarrel'
236            }
237
238        s = Deserializer({"AppleBarrel": AppleBarrel})
239        result = s(AppleBarrel, basic_xml, "application/xml")
240
241        assert result.good_apples == ["granny", "fuji"]
242
243    def test_list_not_wrapped_items_name_basic_types(self):
244        """Test XML list and no wrap, items is basic type and there is itemsName.
245        """
246
247        basic_xml = """<?xml version="1.0"?>
248            <AppleBarrel>
249                <Apple>granny</Apple>
250                <Apple>fuji</Apple>
251            </AppleBarrel>"""
252
253        class AppleBarrel(Model):
254            _attribute_map = {
255                'good_apples': {'key': 'GoodApples', 'type': '[str]', 'xml': {'name': 'GoodApples', 'itemsName': 'Apple'}},
256            }
257            _xml_map = {
258                'name': 'AppleBarrel'
259            }
260
261        s = Deserializer({"AppleBarrel": AppleBarrel})
262        result = s(AppleBarrel, basic_xml, "application/xml")
263
264        assert result.good_apples == ["granny", "fuji"]
265
266    def test_list_wrapped_basic_types(self):
267        """Test XML list and wrap, items is basic type and there is no itemsName.
268        """
269
270        basic_xml = """<?xml version="1.0"?>
271            <AppleBarrel>
272                <GoodApples>
273                  <GoodApples>granny</GoodApples>
274                  <GoodApples>fuji</GoodApples>
275                </GoodApples>
276            </AppleBarrel>"""
277
278        class AppleBarrel(Model):
279            _attribute_map = {
280                'good_apples': {'key': 'GoodApples', 'type': '[str]', 'xml': {'name': 'GoodApples', 'wrapped': True}},
281            }
282            _xml_map = {
283                'name': 'AppleBarrel'
284            }
285
286        s = Deserializer({"AppleBarrel": AppleBarrel})
287        result = s(AppleBarrel, basic_xml, "application/xml")
288
289        assert result.good_apples == ["granny", "fuji"]
290
291    def test_list_not_wrapped_basic_types(self):
292        """Test XML list and no wrap, items is basic type and there is no itemsName.
293        """
294
295        basic_xml = """<?xml version="1.0"?>
296            <AppleBarrel>
297                <GoodApples>granny</GoodApples>
298                <GoodApples>fuji</GoodApples>
299            </AppleBarrel>"""
300
301        class AppleBarrel(Model):
302            _attribute_map = {
303                'good_apples': {'key': 'GoodApples', 'type': '[str]', 'xml': {'name': 'GoodApples'}},
304            }
305            _xml_map = {
306                'name': 'AppleBarrel'
307            }
308
309        s = Deserializer({"AppleBarrel": AppleBarrel})
310        result = s(AppleBarrel, basic_xml, "application/xml")
311
312        assert result.good_apples == ["granny", "fuji"]
313
314
315    def test_list_wrapped_items_name_complex_types(self):
316        """Test XML list and wrap, items is ref and there is itemsName.
317        """
318
319        basic_xml = """<?xml version="1.0"?>
320            <AppleBarrel>
321                <GoodApples>
322                  <Apple name="granny"/>
323                  <Apple name="fuji"/>
324                </GoodApples>
325            </AppleBarrel>"""
326
327        class AppleBarrel(Model):
328            _attribute_map = {
329                'good_apples': {'key': 'GoodApples', 'type': '[Apple]', 'xml': {'name': 'GoodApples', 'wrapped': True, 'itemsName': 'Apple'}},
330            }
331            _xml_map = {
332                'name': 'AppleBarrel'
333            }
334
335        class Apple(Model):
336            _attribute_map = {
337                'name': {'key': 'name', 'type': 'str', 'xml':{'name': 'name', 'attr': True}},
338            }
339            _xml_map = {
340                'name': 'Pomme' # Should be ignored, since "itemsName" is defined
341            }
342
343        s = Deserializer({"AppleBarrel": AppleBarrel, "Apple": Apple})
344        result = s('AppleBarrel', basic_xml, "application/xml")
345
346        assert [apple.name for apple in result.good_apples] == ["granny", "fuji"]
347
348    def test_list_not_wrapped_items_name_complex_types(self):
349        """Test XML list and wrap, items is ref and there is itemsName.
350        """
351
352        basic_xml = """<?xml version="1.0"?>
353            <AppleBarrel>
354                <Apple name="granny"/>
355                <Apple name="fuji"/>
356            </AppleBarrel>"""
357
358        class AppleBarrel(Model):
359            _attribute_map = {
360                # Pomme should be ignored, since it's invalid to define itemsName for a $ref type
361                'good_apples': {'key': 'GoodApples', 'type': '[Apple]', 'xml': {'name': 'GoodApples', 'itemsName': 'Pomme'}},
362            }
363            _xml_map = {
364                'name': 'AppleBarrel'
365            }
366
367        class Apple(Model):
368            _attribute_map = {
369                'name': {'key': 'name', 'type': 'str', 'xml':{'name': 'name', 'attr': True}},
370            }
371            _xml_map = {
372                'name': 'Apple'
373            }
374
375        s = Deserializer({"AppleBarrel": AppleBarrel, "Apple": Apple})
376        result = s(AppleBarrel, basic_xml, "application/xml")
377
378        assert [apple.name for apple in result.good_apples] == ["granny", "fuji"]
379
380    def test_list_wrapped_complex_types(self):
381        """Test XML list and wrap, items is ref and there is no itemsName.
382        """
383
384        basic_xml = """<?xml version="1.0"?>
385            <AppleBarrel>
386                <GoodApples>
387                  <Apple name="granny"/>
388                  <Apple name="fuji"/>
389                </GoodApples>
390            </AppleBarrel>"""
391
392        class AppleBarrel(Model):
393            _attribute_map = {
394                'good_apples': {'key': 'GoodApples', 'type': '[Apple]', 'xml': {'name': 'GoodApples', 'wrapped': True}},
395            }
396            _xml_map = {
397                'name': 'AppleBarrel'
398            }
399
400        class Apple(Model):
401            _attribute_map = {
402                'name': {'key': 'name', 'type': 'str', 'xml':{'name': 'name', 'attr': True}},
403            }
404            _xml_map = {
405                'name': 'Apple'
406            }
407
408        s = Deserializer({"AppleBarrel": AppleBarrel, "Apple": Apple})
409        result = s(AppleBarrel, basic_xml, "application/xml")
410
411        assert [apple.name for apple in result.good_apples] == ["granny", "fuji"]
412
413    def test_list_not_wrapped_complex_types(self):
414        """Test XML list and wrap, items is ref and there is no itemsName.
415        """
416
417        basic_xml = """<?xml version="1.0"?>
418            <AppleBarrel>
419                <Apple name="granny"/>
420                <Apple name="fuji"/>
421            </AppleBarrel>"""
422
423        class AppleBarrel(Model):
424            _attribute_map = {
425                # Name is ignored if wrapped is False
426                'good_apples': {'key': 'GoodApples', 'type': '[Apple]', 'xml': {'name': 'GoodApples'}},
427            }
428            _xml_map = {
429                'name': 'AppleBarrel'
430            }
431
432        class Apple(Model):
433            _attribute_map = {
434                'name': {'key': 'name', 'type': 'str', 'xml':{'name': 'name', 'attr': True}},
435            }
436            _xml_map = {
437                'name': 'Apple'
438            }
439
440        s = Deserializer({"AppleBarrel": AppleBarrel, "Apple": Apple})
441        result = s(AppleBarrel, basic_xml, "application/xml")
442
443        assert [apple.name for apple in result.good_apples] == ["granny", "fuji"]
444
445    def test_basic_additional_properties(self):
446        """Test an ultra basic XML."""
447        basic_xml = """<?xml version="1.0"?>
448            <Metadata>
449              <number>1</number>
450              <name>bob</name>
451            </Metadata>"""
452
453        class XmlModel(Model):
454
455            _attribute_map = {
456                'additional_properties': {'key': '', 'type': '{str}', 'xml': {'name': 'additional_properties'}},
457                'encrypted': {'key': 'Encrypted', 'type': 'str', 'xml': {'name': 'Encrypted', 'attr': True}},
458            }
459            _xml_map = {
460                'name': 'Metadata'
461            }
462
463            def __init__(self, **kwargs):
464                super(XmlModel, self).__init__(**kwargs)
465                self.additional_properties = kwargs.get('additional_properties', None)
466                self.encrypted = kwargs.get('encrypted', None)
467
468        s = Deserializer({"XmlModel": XmlModel})
469        result = s(XmlModel, basic_xml, "application/xml")
470
471        assert result.additional_properties == {'name': 'bob', 'number': '1'}
472        assert result.encrypted is None
473
474    def test_basic_namespace(self):
475        """Test an ultra basic XML."""
476        basic_xml = """<?xml version="1.0"?>
477            <Data xmlns:fictional="http://characters.example.com">
478                <fictional:Age>37</fictional:Age>
479            </Data>"""
480
481        class XmlModel(Model):
482            _attribute_map = {
483                'age': {'key': 'age', 'type': 'int', 'xml':{'name': 'Age', 'prefix':'fictional','ns':'http://characters.example.com'}},
484            }
485            _xml_map = {
486                'name': 'Data'
487            }
488
489        s = Deserializer({"XmlModel": XmlModel})
490        result = s(XmlModel, basic_xml, "application/xml")
491
492        assert result.age == 37
493
494    def test_complex_namespace(self):
495        """Test recursive namespace."""
496        basic_xml = """<?xml version="1.0"?>
497            <entry xmlns="http://www.w3.org/2005/Atom" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
498                <author>
499                    <name>lmazuel</name>
500                </author>
501                <AuthorizationRules xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect">
502                    <AuthorizationRule i:type="SharedAccessAuthorizationRule">
503                        <KeyName>testpolicy</KeyName>
504                    </AuthorizationRule>
505                </AuthorizationRules>
506                <CountDetails xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect">
507                    <d2p1:ActiveMessageCount xmlns:d2p1="http://schemas.microsoft.com/netservices/2011/06/servicebus">12</d2p1:ActiveMessageCount>
508                </CountDetails>
509            </entry>"""
510
511        class XmlRoot(Model):
512            _attribute_map = {
513                'author': {'key': 'author', 'type': 'QueueDescriptionResponseAuthor'},
514                'authorization_rules': {'key': 'AuthorizationRules', 'type': '[AuthorizationRule]', 'xml': {'ns': 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect', 'wrapped': True, 'itemsNs': 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect'}},
515                'message_count_details': {'key': 'MessageCountDetails', 'type': 'MessageCountDetails'},
516            }
517            _xml_map = {
518                'name': 'entry', 'ns': 'http://www.w3.org/2005/Atom'
519            }
520
521        class QueueDescriptionResponseAuthor(Model):
522            _attribute_map = {
523                'name': {'key': 'name', 'type': 'str', 'xml': {'ns': 'http://www.w3.org/2005/Atom'}},
524            }
525            _xml_map = {
526                'ns': 'http://www.w3.org/2005/Atom'
527            }
528
529        class AuthorizationRule(Model):
530            _attribute_map = {
531                'type': {'key': 'type', 'type': 'str', 'xml': {'attr': True, 'prefix': 'i', 'ns': 'http://www.w3.org/2001/XMLSchema-instance'}},
532                'key_name': {'key': 'KeyName', 'type': 'str', 'xml': {'ns': 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect'}},
533            }
534            _xml_map = {
535                'ns': 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect'
536            }
537
538        class MessageCountDetails(Model):
539            _attribute_map = {
540                'active_message_count': {'key': 'ActiveMessageCount', 'type': 'int', 'xml': {'prefix': 'd2p1', 'ns': 'http://schemas.microsoft.com/netservices/2011/06/servicebus'}},
541            }
542            _xml_map = {
543                'name': 'CountDetails', 'ns': 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect'
544            }
545
546
547        s = Deserializer({
548            "XmlRoot": XmlRoot,
549            "QueueDescriptionResponseAuthor": QueueDescriptionResponseAuthor,
550            "AuthorizationRule": AuthorizationRule,
551            "MessageCountDetails": MessageCountDetails,
552        })
553        result = s(XmlRoot, basic_xml, "application/xml")
554
555        assert result.author.name == "lmazuel"
556        assert result.authorization_rules[0].key_name == "testpolicy"
557        assert result.authorization_rules[0].type == "SharedAccessAuthorizationRule"
558        assert result.message_count_details.active_message_count == 12
559
560    def test_polymorphic_deserialization(self):
561
562        basic_xml = """<?xml version="1.0"?>
563            <entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
564                <Filter xsi:type="CorrelationFilter">
565                    <CorrelationId>12</CorrelationId>
566                </Filter>
567            </entry>"""
568
569        class XmlRoot(Model):
570            _attribute_map = {
571                'filter': {'key': 'Filter', 'type': 'RuleFilter'},
572            }
573            _xml_map = {
574                'name': 'entry'
575            }
576
577        class RuleFilter(Model):
578            _attribute_map = {
579                'type': {'key': 'type', 'type': 'str', 'xml': {'attr': True, 'prefix': 'xsi', 'ns': 'http://www.w3.org/2001/XMLSchema-instance'}},
580            }
581
582            _subtype_map = {
583                'type': {'CorrelationFilter': 'CorrelationFilter', 'SqlFilter': 'SqlFilter'}
584            }
585            _xml_map = {
586                'name': 'Filter'
587            }
588
589        class CorrelationFilter(RuleFilter):
590            _attribute_map = {
591                'type': {'key': 'type', 'type': 'str', 'xml': {'attr': True, 'prefix': 'xsi', 'ns': 'http://www.w3.org/2001/XMLSchema-instance'}},
592                'correlation_id': {'key': 'CorrelationId', 'type': 'int'},
593            }
594
595            def __init__(
596                self,
597                correlation_id = None,
598                **kwargs
599            ):
600                super(CorrelationFilter, self).__init__(**kwargs)
601                self.type = 'CorrelationFilter'
602                self.correlation_id = correlation_id
603
604        class SqlFilter(RuleFilter):
605            _attribute_map = {
606                'type': {'key': 'type', 'type': 'str', 'xml': {'attr': True, 'prefix': 'xsi', 'ns': 'http://www.w3.org/2001/XMLSchema-instance'}},
607            }
608
609            def __init__(
610                self,
611                **kwargs
612            ):
613                pytest.fail("Don't instantiate me")
614
615        s = Deserializer({
616            "XmlRoot": XmlRoot,
617            "RuleFilter": RuleFilter,
618            "SqlFilter": SqlFilter,
619            "CorrelationFilter": CorrelationFilter,
620        })
621        result = s(XmlRoot, basic_xml, "application/xml")
622
623        assert isinstance(result.filter, CorrelationFilter)
624        assert result.filter.correlation_id == 12
625
626
627class TestXmlSerialization:
628
629    def test_basic(self):
630        """Test an ultra basic XML."""
631        basic_xml = ET.fromstring("""<?xml version="1.0"?>
632            <Data country="france">
633                <Age>37</Age>
634            </Data>""")
635
636        class XmlModel(Model):
637            _attribute_map = {
638                'age': {'key': 'age', 'type': 'int', 'xml':{'name': 'Age'}},
639                'country': {'key': 'country', 'type': 'str', 'xml':{'name': 'country', 'attr': True}},
640            }
641            _xml_map = {
642                'name': 'Data'
643            }
644
645        mymodel = XmlModel(
646            age=37,
647            country="france"
648        )
649
650        s = Serializer({"XmlModel": XmlModel})
651        rawxml = s.body(mymodel, 'XmlModel')
652
653        assert_xml_equals(rawxml, basic_xml)
654
655    def test_basic_unicode(self):
656        """Test a XML with unicode."""
657        basic_xml = ET.fromstring(u"""<?xml version="1.0" encoding="utf-8"?>
658            <Data language="français"/>""".encode("utf-8"))
659
660        class XmlModel(Model):
661            _attribute_map = {
662                'language': {'key': 'language', 'type': 'str', 'xml':{'name': 'language', 'attr': True}},
663            }
664            _xml_map = {
665                'name': 'Data'
666            }
667
668        mymodel = XmlModel(
669            language=u"français"
670        )
671
672        s = Serializer({"XmlModel": XmlModel})
673        rawxml = s.body(mymodel, 'XmlModel')
674
675        assert_xml_equals(rawxml, basic_xml)
676
677    def test_nested_unicode(self):
678
679        class XmlModel(Model):
680            _attribute_map = {
681                'message_text': {'key': 'MessageText', 'type': 'str', 'xml': {'name': 'MessageText'}},
682            }
683
684            _xml_map = {
685                'name': 'Message'
686            }
687
688        mymodel_no_unicode = XmlModel(message_text=u'message1')
689        s = Serializer({"XmlModel": XmlModel})
690        body = s.body(mymodel_no_unicode, 'XmlModel')
691        xml_content = ET.tostring(body, encoding="utf8")
692        assert xml_content == b"<?xml version='1.0' encoding='utf8'?>\n<Message><MessageText>message1</MessageText></Message>"
693
694        mymodel_with_unicode = XmlModel(message_text=u'message1㚈')
695        s = Serializer({"XmlModel": XmlModel})
696        body = s.body(mymodel_with_unicode, 'XmlModel')
697        xml_content = ET.tostring(body, encoding="utf8")
698        assert xml_content == b"<?xml version='1.0' encoding='utf8'?>\n<Message><MessageText>message1\xe3\x9a\x88</MessageText></Message>"
699
700    @pytest.mark.skipif(sys.version_info < (3,6),
701                        reason="Dict ordering not guaranted before 3.6, makes this complicated to test.")
702    def test_add_prop(self):
703        """Test addProp as a dict.
704        """
705        basic_xml = ET.fromstring("""<?xml version="1.0"?>
706            <Data>
707                <Metadata>
708                  <Key1>value1</Key1>
709                  <Key2>value2</Key2>
710                </Metadata>
711            </Data>""")
712
713        class XmlModel(Model):
714            _attribute_map = {
715                'metadata': {'key': 'Metadata', 'type': '{str}', 'xml': {'name': 'Metadata'}},
716            }
717            _xml_map = {
718                'name': 'Data'
719            }
720
721        mymodel = XmlModel(
722            metadata={
723                'Key1': 'value1',
724                'Key2': 'value2',
725            }
726        )
727
728        s = Serializer({"XmlModel": XmlModel})
729        rawxml = s.body(mymodel, 'XmlModel')
730
731        assert_xml_equals(rawxml, basic_xml)
732
733    def test_object(self):
734        """Test serialize object as is.
735        """
736        basic_xml = ET.fromstring("""<?xml version="1.0"?>
737            <Data country="france">
738                <Age>37</Age>
739            </Data>""")
740
741        s = Serializer()
742        rawxml = s.body(basic_xml, 'object')
743
744        # It should actually be the same object, should not even try to touch it
745        assert rawxml is basic_xml
746
747    @pytest.mark.skipif(sys.version_info < (3,6),
748                        reason="Unstable before python3.6 for some reasons")
749    def test_type_basic(self):
750        """Test some types."""
751        basic_xml = ET.fromstring("""<?xml version="1.0"?>
752            <Data>
753                <Age>37</Age>
754                <Enabled>true</Enabled>
755            </Data>""")
756
757        class XmlModel(Model):
758            _attribute_map = {
759                'age': {'key': 'age', 'type': 'int', 'xml':{'name': 'Age'}},
760                'enabled': {'key': 'enabled', 'type': 'bool', 'xml':{'name': 'Enabled'}},
761            }
762            _xml_map = {
763                'name': 'Data'
764            }
765
766        mymodel = XmlModel(
767            age=37,
768            enabled=True
769        )
770
771        s = Serializer({"XmlModel": XmlModel})
772        rawxml = s.body(mymodel, 'XmlModel')
773
774        assert_xml_equals(rawxml, basic_xml)
775
776    def test_basic_text(self):
777        """Test a XML with unicode."""
778        basic_xml = ET.fromstring("""<?xml version="1.0" encoding="utf-8"?>
779            <Data language="english">I am text</Data>""")
780
781        class XmlModel(Model):
782            _attribute_map = {
783                'language': {'key': 'language', 'type': 'str', 'xml':{'name': 'language', 'attr': True}},
784                'content': {'key': 'content', 'type': 'str', 'xml':{'text': True}},
785            }
786            _xml_map = {
787                'name': 'Data'
788            }
789
790        mymodel = XmlModel(
791            language="english",
792            content="I am text"
793        )
794
795        s = Serializer({"XmlModel": XmlModel})
796        rawxml = s.body(mymodel, 'XmlModel')
797
798        assert_xml_equals(rawxml, basic_xml)
799
800
801    def test_direct_array(self):
802        """Test an ultra basic XML."""
803        basic_xml = ET.fromstring("""<?xml version="1.0"?>
804            <bananas>
805               <Data country="france"/>
806            </bananas>
807            """)
808
809        class XmlModel(Model):
810            _attribute_map = {
811                'country': {'key': 'country', 'type': 'str', 'xml':{'name': 'country', 'attr': True}},
812            }
813            _xml_map = {
814                'name': 'Data'
815            }
816
817        mymodel = XmlModel(
818            country="france"
819        )
820
821        s = Serializer({"XmlModel": XmlModel})
822        rawxml = s.body(
823            [mymodel],
824            '[XmlModel]',
825            serialization_ctxt={'xml': {'name': 'bananas', 'wrapped': True}}
826        )
827
828        assert_xml_equals(rawxml, basic_xml)
829
830    def test_list_wrapped_basic_types(self):
831        """Test XML list and wrap, items is basic type and there is no itemsName.
832        """
833
834        basic_xml = ET.fromstring("""<?xml version="1.0"?>
835            <AppleBarrel>
836                <GoodApples>
837                  <GoodApples>granny</GoodApples>
838                  <GoodApples>fuji</GoodApples>
839                </GoodApples>
840            </AppleBarrel>""")
841
842        class AppleBarrel(Model):
843            _attribute_map = {
844                'good_apples': {'key': 'GoodApples', 'type': '[str]', 'xml': {'name': 'GoodApples', 'wrapped': True}},
845            }
846            _xml_map = {
847                'name': 'AppleBarrel'
848            }
849
850        mymodel = AppleBarrel(
851            good_apples=['granny', 'fuji']
852        )
853
854        s = Serializer({"AppleBarrel": AppleBarrel})
855        rawxml = s.body(mymodel, 'AppleBarrel')
856
857        assert_xml_equals(rawxml, basic_xml)
858
859    def test_list_not_wrapped_basic_types(self):
860        """Test XML list and no wrap, items is basic type and there is no itemsName.
861        """
862
863        basic_xml = ET.fromstring("""<?xml version="1.0"?>
864            <AppleBarrel>
865                <GoodApples>granny</GoodApples>
866                <GoodApples>fuji</GoodApples>
867            </AppleBarrel>""")
868
869        class AppleBarrel(Model):
870            _attribute_map = {
871                'good_apples': {'key': 'GoodApples', 'type': '[str]', 'xml': {'name': 'GoodApples'}},
872            }
873            _xml_map = {
874                'name': 'AppleBarrel'
875            }
876
877        mymodel = AppleBarrel(
878            good_apples=['granny', 'fuji']
879        )
880
881        s = Serializer({"AppleBarrel": AppleBarrel})
882        rawxml = s.body(mymodel, 'AppleBarrel')
883
884        assert_xml_equals(rawxml, basic_xml)
885
886    def test_list_wrapped_items_name_complex_types(self):
887        """Test XML list and wrap, items is ref and there is itemsName.
888        """
889
890        basic_xml = ET.fromstring("""<?xml version="1.0"?>
891            <AppleBarrel>
892                <GoodApples>
893                  <Apple name="granny"/>
894                  <Apple name="fuji"/>
895                </GoodApples>
896            </AppleBarrel>""")
897
898        class AppleBarrel(Model):
899            _attribute_map = {
900                # Pomme should be ignored, since it's invalid to define itemsName for a $ref type
901                'good_apples': {'key': 'GoodApples', 'type': '[Apple]', 'xml': {'name': 'GoodApples', 'wrapped': True, 'itemsName': 'Pomme'}},
902            }
903            _xml_map = {
904                'name': 'AppleBarrel'
905            }
906
907        class Apple(Model):
908            _attribute_map = {
909                'name': {'key': 'name', 'type': 'str', 'xml':{'name': 'name', 'attr': True}},
910            }
911            _xml_map = {
912                'name': 'Apple'
913            }
914
915        mymodel = AppleBarrel(
916            good_apples=[
917                Apple(name='granny'),
918                Apple(name='fuji')
919            ]
920        )
921
922        s = Serializer({"AppleBarrel": AppleBarrel, "Apple": Apple})
923        rawxml = s.body(mymodel, 'AppleBarrel')
924
925        assert_xml_equals(rawxml, basic_xml)
926
927    def test_list_not_wrapped_items_name_complex_types(self):
928        """Test XML list and wrap, items is ref and there is itemsName.
929        """
930
931        basic_xml = ET.fromstring("""<?xml version="1.0"?>
932            <AppleBarrel>
933                <Apple name="granny"/>
934                <Apple name="fuji"/>
935            </AppleBarrel>""")
936
937        class AppleBarrel(Model):
938            _attribute_map = {
939                # Pomme should be ignored, since it's invalid to define itemsName for a $ref type
940                'good_apples': {'key': 'GoodApples', 'type': '[Apple]', 'xml': {'name': 'GoodApples', 'itemsName': 'Pomme'}},
941            }
942            _xml_map = {
943                'name': 'AppleBarrel'
944            }
945
946        class Apple(Model):
947            _attribute_map = {
948                'name': {'key': 'name', 'type': 'str', 'xml':{'name': 'name', 'attr': True}},
949            }
950            _xml_map = {
951                'name': 'Apple'
952            }
953
954        mymodel = AppleBarrel(
955            good_apples=[
956                Apple(name='granny'),
957                Apple(name='fuji')
958            ]
959        )
960
961        s = Serializer({"AppleBarrel": AppleBarrel, "Apple": Apple})
962        rawxml = s.body(mymodel, 'AppleBarrel')
963
964        assert_xml_equals(rawxml, basic_xml)
965
966    def test_list_wrapped_complex_types(self):
967        """Test XML list and wrap, items is ref and there is no itemsName.
968        """
969
970        basic_xml = ET.fromstring("""<?xml version="1.0"?>
971            <AppleBarrel>
972                <GoodApples>
973                  <Apple name="granny"/>
974                  <Apple name="fuji"/>
975                </GoodApples>
976            </AppleBarrel>""")
977
978        class AppleBarrel(Model):
979            _attribute_map = {
980                'good_apples': {'key': 'GoodApples', 'type': '[Apple]', 'xml': {'name': 'GoodApples', 'wrapped': True}},
981            }
982            _xml_map = {
983                'name': 'AppleBarrel'
984            }
985
986        class Apple(Model):
987            _attribute_map = {
988                'name': {'key': 'name', 'type': 'str', 'xml':{'name': 'name', 'attr': True}},
989            }
990            _xml_map = {
991                'name': 'Apple'
992            }
993
994        mymodel = AppleBarrel(
995            good_apples=[
996                Apple(name='granny'),
997                Apple(name='fuji')
998            ]
999        )
1000
1001        s = Serializer({"AppleBarrel": AppleBarrel, "Apple": Apple})
1002        rawxml = s.body(mymodel, 'AppleBarrel')
1003
1004        assert_xml_equals(rawxml, basic_xml)
1005
1006    def test_list_not_wrapped_complex_types(self):
1007        """Test XML list and wrap, items is ref and there is no itemsName.
1008        """
1009
1010        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1011            <AppleBarrel>
1012                <Apple name="granny"/>
1013                <Apple name="fuji"/>
1014            </AppleBarrel>""")
1015
1016        class AppleBarrel(Model):
1017            _attribute_map = {
1018                # Name is ignored if "wrapped" is False
1019                'good_apples': {'key': 'GoodApples', 'type': '[Apple]', 'xml': {'name': 'GoodApples'}},
1020            }
1021            _xml_map = {
1022                'name': 'AppleBarrel'
1023            }
1024
1025        class Apple(Model):
1026            _attribute_map = {
1027                'name': {'key': 'name', 'type': 'str', 'xml':{'name': 'name', 'attr': True}},
1028            }
1029            _xml_map = {
1030                'name': 'Apple'
1031            }
1032
1033        mymodel = AppleBarrel(
1034            good_apples=[
1035                Apple(name='granny'),
1036                Apple(name='fuji')
1037            ]
1038        )
1039
1040        s = Serializer({"AppleBarrel": AppleBarrel, "Apple": Apple})
1041        rawxml = s.body(mymodel, 'AppleBarrel')
1042
1043        assert_xml_equals(rawxml, basic_xml)
1044
1045    @pytest.mark.skipif(sys.version_info < (3,6),
1046                        reason="Unstable before python3.6 for some reasons")
1047    def test_two_complex_same_type(self):
1048        """Two different attribute are same type
1049        """
1050
1051        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1052            <AppleBarrel>
1053                <EuropeanApple name="granny"/>
1054                <USAApple name="fuji"/>
1055            </AppleBarrel>""")
1056
1057        class AppleBarrel(Model):
1058            _attribute_map = {
1059                'eu_apple': {'key': 'EuropeanApple', 'type': 'Apple', 'xml': {'name': 'EuropeanApple'}},
1060                'us_apple': {'key': 'USAApple', 'type': 'Apple', 'xml': {'name': 'USAApple'}},
1061            }
1062            _xml_map = {
1063                'name': 'AppleBarrel'
1064            }
1065
1066        class Apple(Model):
1067            _attribute_map = {
1068                'name': {'key': 'name', 'type': 'str', 'xml':{'name': 'name', 'attr': True}},
1069            }
1070            _xml_map = {
1071            }
1072
1073        mymodel = AppleBarrel(
1074            eu_apple=Apple(name='granny'),
1075            us_apple=Apple(name='fuji'),
1076        )
1077
1078        s = Serializer({"AppleBarrel": AppleBarrel, "Apple": Apple})
1079        rawxml = s.body(mymodel, 'AppleBarrel')
1080
1081        assert_xml_equals(rawxml, basic_xml)
1082
1083
1084    def test_basic_namespace(self):
1085        """Test an ultra basic XML."""
1086        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1087            <Data xmlns:fictional="http://characters.example.com">
1088                <fictional:Age>37</fictional:Age>
1089            </Data>""")
1090
1091        class XmlModel(Model):
1092            _attribute_map = {
1093                'age': {'key': 'age', 'type': 'int', 'xml':{'name': 'Age', 'prefix':'fictional','ns':'http://characters.example.com'}},
1094            }
1095            _xml_map = {
1096                'name': 'Data'
1097            }
1098
1099        mymodel = XmlModel(
1100            age=37,
1101        )
1102
1103        s = Serializer({"XmlModel": XmlModel})
1104        rawxml = s.body(mymodel, 'XmlModel')
1105
1106        assert_xml_equals(rawxml, basic_xml)
1107
1108    def test_basic_is_xml(self):
1109        """Test an ultra basic XML."""
1110        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1111            <Data country="france">
1112                <Age>37</Age>
1113            </Data>""")
1114
1115        class XmlModel(Model):
1116            _attribute_map = {
1117                'age': {'key': 'age', 'type': 'int', 'xml':{'name': 'Age'}},
1118                'country': {'key': 'country', 'type': 'str', 'xml':{'name': 'country', 'attr': True}},
1119            }
1120            _xml_map = {
1121                'name': 'Data'
1122            }
1123
1124        mymodel = XmlModel(
1125            age=37,
1126            country="france",
1127        )
1128
1129        s = Serializer({"XmlModel": XmlModel})
1130        rawxml = s.body(mymodel, 'XmlModel', is_xml=True)
1131
1132        assert_xml_equals(rawxml, basic_xml)
1133
1134    def test_basic_unicode_is_xml(self):
1135        """Test a XML with unicode."""
1136        basic_xml = ET.fromstring(u"""<?xml version="1.0" encoding="utf-8"?>
1137            <Data language="français"/>""".encode("utf-8"))
1138
1139        class XmlModel(Model):
1140            _attribute_map = {
1141                'language': {'key': 'language', 'type': 'str', 'xml':{'name': 'language', 'attr': True}},
1142            }
1143            _xml_map = {
1144                'name': 'Data'
1145            }
1146
1147        mymodel = XmlModel(
1148            language=u"français"
1149        )
1150
1151        s = Serializer({"XmlModel": XmlModel})
1152        rawxml = s.body(mymodel, 'XmlModel', is_xml=True)
1153
1154        assert_xml_equals(rawxml, basic_xml)
1155
1156
1157    @pytest.mark.skipif(sys.version_info < (3,6),
1158                        reason="Dict ordering not guaranted before 3.6, makes this complicated to test.")
1159    def test_add_prop_is_xml(self):
1160        """Test addProp as a dict.
1161        """
1162        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1163            <Data>
1164                <Metadata>
1165                  <Key1>value1</Key1>
1166                  <Key2>value2</Key2>
1167                </Metadata>
1168            </Data>""")
1169
1170        class XmlModel(Model):
1171            _attribute_map = {
1172                'metadata': {'key': 'Metadata', 'type': '{str}', 'xml': {'name': 'Metadata'}},
1173            }
1174            _xml_map = {
1175                'name': 'Data'
1176            }
1177
1178        mymodel = XmlModel(
1179            metadata={
1180                'Key1': 'value1',
1181                'Key2': 'value2',
1182            }
1183        )
1184
1185        s = Serializer({"XmlModel": XmlModel})
1186        rawxml = s.body(mymodel, 'XmlModel', is_xml=True)
1187
1188        assert_xml_equals(rawxml, basic_xml)
1189
1190    def test_object_is_xml(self):
1191        """Test serialize object as is.
1192        """
1193        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1194            <Data country="france">
1195                <Age>37</Age>
1196            </Data>""")
1197
1198        s = Serializer()
1199        rawxml = s.body(basic_xml, 'object', is_xml=True)
1200
1201        # It should actually be the same object, should not even try to touch it
1202        assert rawxml is basic_xml
1203
1204    @pytest.mark.skipif(sys.version_info < (3,6),
1205                        reason="Unstable before python3.6 for some reasons")
1206    def test_type_basic_is_xml(self):
1207        """Test some types."""
1208        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1209            <Data>
1210                <Age>37</Age>
1211                <Enabled>true</Enabled>
1212            </Data>""")
1213
1214        class XmlModel(Model):
1215            _attribute_map = {
1216                'age': {'key': 'age', 'type': 'int', 'xml':{'name': 'Age'}},
1217                'enabled': {'key': 'enabled', 'type': 'bool', 'xml':{'name': 'Enabled'}},
1218            }
1219            _xml_map = {
1220                'name': 'Data'
1221            }
1222
1223        mymodel = XmlModel(
1224            age=37,
1225            enabled=True
1226        )
1227
1228        s = Serializer({"XmlModel": XmlModel})
1229        rawxml = s.body(mymodel, 'XmlModel', is_xml=True)
1230
1231        assert_xml_equals(rawxml, basic_xml)
1232
1233    def test_direct_array_is_xml(self):
1234        """Test an ultra basic XML."""
1235        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1236            <bananas>
1237               <Data country="france"/>
1238            </bananas>
1239            """)
1240
1241        class XmlModel(Model):
1242            _attribute_map = {
1243                'country': {'key': 'country', 'type': 'str', 'xml':{'name': 'country', 'attr': True}},
1244            }
1245            _xml_map = {
1246                'name': 'Data'
1247            }
1248
1249        mymodel = XmlModel(
1250            country="france"
1251        )
1252
1253        s = Serializer({"XmlModel": XmlModel})
1254        rawxml = s.body(
1255            [mymodel],
1256            '[XmlModel]',
1257            serialization_ctxt={'xml': {'name': 'bananas', 'wrapped': True}},
1258            is_xml=True
1259        )
1260
1261        assert_xml_equals(rawxml, basic_xml)
1262
1263    def test_list_wrapped_basic_types_is_xml(self):
1264        """Test XML list and wrap, items is basic type and there is no itemsName.
1265        """
1266
1267        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1268            <AppleBarrel>
1269                <GoodApples>
1270                  <GoodApples>granny</GoodApples>
1271                  <GoodApples>fuji</GoodApples>
1272                </GoodApples>
1273            </AppleBarrel>""")
1274
1275        class AppleBarrel(Model):
1276            _attribute_map = {
1277                'good_apples': {'key': 'GoodApples', 'type': '[str]', 'xml': {'name': 'GoodApples', 'wrapped': True}},
1278            }
1279            _xml_map = {
1280                'name': 'AppleBarrel'
1281            }
1282
1283        mymodel = AppleBarrel(
1284            good_apples=['granny', 'fuji']
1285        )
1286
1287        s = Serializer({"AppleBarrel": AppleBarrel})
1288        rawxml = s.body(mymodel, 'AppleBarrel', is_xml=True)
1289
1290        assert_xml_equals(rawxml, basic_xml)
1291
1292    def test_list_not_wrapped_basic_types_is_xml(self):
1293        """Test XML list and no wrap, items is basic type and there is no itemsName.
1294        """
1295
1296        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1297            <AppleBarrel>
1298                <GoodApples>granny</GoodApples>
1299                <GoodApples>fuji</GoodApples>
1300            </AppleBarrel>""")
1301
1302        class AppleBarrel(Model):
1303            _attribute_map = {
1304                'good_apples': {'key': 'GoodApples', 'type': '[str]', 'xml': {'name': 'GoodApples'}},
1305            }
1306            _xml_map = {
1307                'name': 'AppleBarrel'
1308            }
1309
1310        mymodel = AppleBarrel(
1311            good_apples=['granny', 'fuji']
1312        )
1313
1314        s = Serializer({"AppleBarrel": AppleBarrel})
1315        rawxml = s.body(mymodel, 'AppleBarrel', is_xml=True)
1316
1317        assert_xml_equals(rawxml, basic_xml)
1318
1319    def test_list_wrapped_items_name_complex_types_is_xml(self):
1320        """Test XML list and wrap, items is ref and there is itemsName.
1321        """
1322
1323        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1324            <AppleBarrel>
1325                <GoodApples>
1326                  <Apple name="granny"/>
1327                  <Apple name="fuji"/>
1328                </GoodApples>
1329            </AppleBarrel>""")
1330
1331        class AppleBarrel(Model):
1332            _attribute_map = {
1333                # Pomme should be ignored, since it's invalid to define itemsName for a $ref type
1334                'good_apples': {'key': 'GoodApples', 'type': '[Apple]', 'xml': {'name': 'GoodApples', 'wrapped': True, 'itemsName': 'Pomme'}},
1335            }
1336            _xml_map = {
1337                'name': 'AppleBarrel'
1338            }
1339
1340        class Apple(Model):
1341            _attribute_map = {
1342                'name': {'key': 'name', 'type': 'str', 'xml':{'name': 'name', 'attr': True}},
1343            }
1344            _xml_map = {
1345                'name': 'Apple'
1346            }
1347
1348        mymodel = AppleBarrel(
1349            good_apples=[
1350                Apple(name='granny'),
1351                Apple(name='fuji')
1352            ]
1353        )
1354
1355        s = Serializer({"AppleBarrel": AppleBarrel, "Apple": Apple})
1356        rawxml = s.body(mymodel, 'AppleBarrel', is_xml=True)
1357
1358        assert_xml_equals(rawxml, basic_xml)
1359
1360    def test_list_not_wrapped_items_name_complex_types_is_xml(self):
1361        """Test XML list and wrap, items is ref and there is itemsName.
1362        """
1363
1364        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1365            <AppleBarrel>
1366                <Apple name="granny"/>
1367                <Apple name="fuji"/>
1368            </AppleBarrel>""")
1369
1370        class AppleBarrel(Model):
1371            _attribute_map = {
1372                # Pomme should be ignored, since it's invalid to define itemsName for a $ref type
1373                'good_apples': {'key': 'GoodApples', 'type': '[Apple]', 'xml': {'name': 'GoodApples', 'itemsName': 'Pomme'}},
1374            }
1375            _xml_map = {
1376                'name': 'AppleBarrel'
1377            }
1378
1379        class Apple(Model):
1380            _attribute_map = {
1381                'name': {'key': 'name', 'type': 'str', 'xml':{'name': 'name', 'attr': True}},
1382            }
1383            _xml_map = {
1384                'name': 'Apple'
1385            }
1386
1387        mymodel = AppleBarrel(
1388            good_apples=[
1389                Apple(name='granny'),
1390                Apple(name='fuji')
1391            ]
1392        )
1393
1394        s = Serializer({"AppleBarrel": AppleBarrel, "Apple": Apple})
1395        rawxml = s.body(mymodel, 'AppleBarrel', is_xml=True)
1396
1397        assert_xml_equals(rawxml, basic_xml)
1398
1399    def test_list_wrapped_complex_types_is_xml(self):
1400        """Test XML list and wrap, items is ref and there is no itemsName.
1401        """
1402
1403        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1404            <AppleBarrel>
1405                <GoodApples>
1406                  <Apple name="granny"/>
1407                  <Apple name="fuji"/>
1408                </GoodApples>
1409            </AppleBarrel>""")
1410
1411        class AppleBarrel(Model):
1412            _attribute_map = {
1413                'good_apples': {'key': 'GoodApples', 'type': '[Apple]', 'xml': {'name': 'GoodApples', 'wrapped': True}},
1414            }
1415            _xml_map = {
1416                'name': 'AppleBarrel'
1417            }
1418
1419        class Apple(Model):
1420            _attribute_map = {
1421                'name': {'key': 'name', 'type': 'str', 'xml':{'name': 'name', 'attr': True}},
1422            }
1423            _xml_map = {
1424                'name': 'Apple'
1425            }
1426
1427        mymodel = AppleBarrel(
1428            good_apples=[
1429                Apple(name='granny'),
1430                Apple(name='fuji')
1431            ]
1432        )
1433
1434        s = Serializer({"AppleBarrel": AppleBarrel, "Apple": Apple})
1435        rawxml = s.body(mymodel, 'AppleBarrel', is_xml=True)
1436
1437        assert_xml_equals(rawxml, basic_xml)
1438
1439    def test_list_not_wrapped_complex_types_is_xml(self):
1440        """Test XML list and wrap, items is ref and there is no itemsName.
1441        """
1442
1443        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1444            <AppleBarrel>
1445                <Apple name="granny"/>
1446                <Apple name="fuji"/>
1447            </AppleBarrel>""")
1448
1449        class AppleBarrel(Model):
1450            _attribute_map = {
1451                # Name is ignored if "wrapped" is False
1452                'good_apples': {'key': 'GoodApples', 'type': '[Apple]', 'xml': {'name': 'GoodApples'}},
1453            }
1454            _xml_map = {
1455                'name': 'AppleBarrel'
1456            }
1457
1458        class Apple(Model):
1459            _attribute_map = {
1460                'name': {'key': 'name', 'type': 'str', 'xml':{'name': 'name', 'attr': True}},
1461            }
1462            _xml_map = {
1463                'name': 'Apple'
1464            }
1465
1466        mymodel = AppleBarrel(
1467            good_apples=[
1468                Apple(name='granny'),
1469                Apple(name='fuji')
1470            ]
1471        )
1472
1473        s = Serializer({"AppleBarrel": AppleBarrel, "Apple": Apple})
1474        rawxml = s.body(mymodel, 'AppleBarrel', is_xml=True)
1475
1476        assert_xml_equals(rawxml, basic_xml)
1477
1478    @pytest.mark.skipif(sys.version_info < (3,6),
1479                        reason="Unstable before python3.6 for some reasons")
1480    def test_two_complex_same_type_is_xml(self):
1481        """Two different attribute are same type
1482        """
1483
1484        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1485            <AppleBarrel>
1486                <EuropeanApple name="granny"/>
1487                <USAApple name="fuji"/>
1488            </AppleBarrel>""")
1489
1490        class AppleBarrel(Model):
1491            _attribute_map = {
1492                'eu_apple': {'key': 'EuropeanApple', 'type': 'Apple', 'xml': {'name': 'EuropeanApple'}},
1493                'us_apple': {'key': 'USAApple', 'type': 'Apple', 'xml': {'name': 'USAApple'}},
1494            }
1495            _xml_map = {
1496                'name': 'AppleBarrel'
1497            }
1498
1499        class Apple(Model):
1500            _attribute_map = {
1501                'name': {'key': 'name', 'type': 'str', 'xml':{'name': 'name', 'attr': True}},
1502            }
1503            _xml_map = {
1504            }
1505
1506        mymodel = AppleBarrel(
1507            eu_apple=Apple(name='granny'),
1508            us_apple=Apple(name='fuji'),
1509        )
1510
1511        s = Serializer({"AppleBarrel": AppleBarrel, "Apple": Apple})
1512        rawxml = s.body(mymodel, 'AppleBarrel', is_xml=True)
1513
1514        assert_xml_equals(rawxml, basic_xml)
1515
1516
1517    def test_basic_namespace_is_xml(self):
1518        """Test an ultra basic XML."""
1519        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1520            <Data xmlns:fictional="http://characters.example.com">
1521                <fictional:Age>37</fictional:Age>
1522            </Data>""")
1523
1524        class XmlModel(Model):
1525            _attribute_map = {
1526                'age': {'key': 'age', 'type': 'int', 'xml':{'name': 'Age', 'prefix':'fictional','ns':'http://characters.example.com'}},
1527            }
1528            _xml_map = {
1529                'name': 'Data'
1530            }
1531
1532        mymodel = XmlModel(
1533            age=37,
1534        )
1535
1536        s = Serializer({"XmlModel": XmlModel})
1537        rawxml = s.body(mymodel, 'XmlModel', is_xml=True)
1538
1539        assert_xml_equals(rawxml, basic_xml)
1540
1541    @pytest.mark.skipif(sys.version_info < (3,6),
1542                        reason="Unstable before python3.6 for some reasons")
1543    def test_complex_namespace(self):
1544        """Test recursive namespace."""
1545        basic_xml = ET.fromstring("""<?xml version="1.0"?>
1546            <entry xmlns="http://www.w3.org/2005/Atom" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
1547                <author>
1548                    <name>lmazuel</name>
1549                </author>
1550                <AuthorizationRules xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect">
1551                    <AuthorizationRule i:type="SharedAccessAuthorizationRule">
1552                        <KeyName>testpolicy</KeyName>
1553                    </AuthorizationRule>
1554                </AuthorizationRules>
1555            </entry>""")
1556
1557        class XmlRoot(Model):
1558            _attribute_map = {
1559                'author': {'key': 'author', 'type': 'QueueDescriptionResponseAuthor'},
1560                'authorization_rules': {'key': 'AuthorizationRules', 'type': '[AuthorizationRule]', 'xml': {'ns': 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect', 'wrapped': True, 'itemsNs': 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect'}},
1561            }
1562            _xml_map = {
1563                'name': 'entry', 'ns': 'http://www.w3.org/2005/Atom'
1564            }
1565
1566        class QueueDescriptionResponseAuthor(Model):
1567            _attribute_map = {
1568                'name': {'key': 'name', 'type': 'str', 'xml': {'ns': 'http://www.w3.org/2005/Atom'}},
1569            }
1570            _xml_map = {
1571                'ns': 'http://www.w3.org/2005/Atom'
1572            }
1573
1574        class AuthorizationRule(Model):
1575            _attribute_map = {
1576                'type': {'key': 'type', 'type': 'str', 'xml': {'attr': True, 'prefix': 'i', 'ns': 'http://www.w3.org/2001/XMLSchema-instance'}},
1577                'key_name': {'key': 'KeyName', 'type': 'str', 'xml': {'ns': 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect'}},
1578            }
1579            _xml_map = {
1580                'ns': 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect'
1581            }
1582
1583        mymodel = XmlRoot(
1584            author = QueueDescriptionResponseAuthor(name = "lmazuel"),
1585            authorization_rules = [AuthorizationRule(
1586                type="SharedAccessAuthorizationRule",
1587                key_name="testpolicy"
1588            )]
1589        )
1590
1591        s = Serializer({
1592            "XmlRoot": XmlRoot,
1593            "QueueDescriptionResponseAuthor": QueueDescriptionResponseAuthor,
1594            "AuthorizationRule": AuthorizationRule,
1595        })
1596        rawxml = s.body(mymodel, 'XmlModel')
1597
1598        assert_xml_equals(rawxml, basic_xml)
1599