diff --git a/docs/parsers.md b/docs/parsers.md index eb58ef8..f23c1fb 100644 --- a/docs/parsers.md +++ b/docs/parsers.md @@ -51,3 +51,52 @@ If you are considering using `XML` for your API, you may want to consider implem Requires the `defusedxml` package to be installed. **.media_type**: `application/xml` + + +# Converting values to native Python data types + +N.B. This section describes behavior that has changed from version 2 upwards. + +Values are *not* converted to native Python, which means that you'll get strings from the default parser. You can implement your own conversion to native data types with a custom Parser that implements a `type_convert(value)` method. This method accepts a single value and returns the converted value. + +In version 1.x and earlier, conversions were done for you. To retain the original behavior, implement a Parser like below and enable this Parser in `DEFAULT_PARSER_CLASSES`: + + import datetime + import decimal + + from rest_framework_xml.parsers import XMLParser + + class MyXMLParser(XMLParser): + + def type_convert(self, value): + if value is None: + return value + + try: + return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S') + except ValueError: + pass + + try: + return int(value) + except ValueError: + pass + + try: + return decimal.Decimal(value) + except decimal.InvalidOperation: + pass + + return value + +Enable this in your settings: + + REST_FRAMEWORK = { + 'DEFAULT_PARSER_CLASSES': ( + 'path.to.MyXMLParser', + ) + } + +Or use it in `parser_classes` or the decorator for individual class based or function based views. + + import path.to.MyXMLParser as XMLParser diff --git a/rest_framework_xml/parsers.py b/rest_framework_xml/parsers.py index 5454356..bd67aa7 100644 --- a/rest_framework_xml/parsers.py +++ b/rest_framework_xml/parsers.py @@ -2,8 +2,6 @@ Provides XML parsing support. """ from __future__ import unicode_literals -import datetime -import decimal from django.conf import settings from django.utils import six @@ -45,7 +43,7 @@ def _xml_convert(self, element): children = list(element) if len(children) == 0: - return self._type_convert(element.text) + return self.type_convert(element.text) else: # if the fist child tag is list-item means all children are list-item if children[0].tag == "list-item": @@ -59,27 +57,9 @@ def _xml_convert(self, element): return data - def _type_convert(self, value): + def type_convert(self, value): """ - Converts the value returned by the XMl parse into the equivalent - Python type + Converts the value returned by the XMl parse into the equivalent Python + type. Override this method in your own Parser to do the conversion. """ - if value is None: - return value - - try: - return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S') - except ValueError: - pass - - try: - return int(value) - except ValueError: - pass - - try: - return decimal.Decimal(value) - except decimal.InvalidOperation: - pass - return value diff --git a/tests/test_parsers.py b/tests/test_parsers.py index 38618a8..df32ff0 100644 --- a/tests/test_parsers.py +++ b/tests/test_parsers.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -import datetime - from django.test import TestCase from django.test.utils import skipUnless @@ -22,10 +20,10 @@ def setUp(self): '' ) self._data = { - 'field_a': 121, + 'field_a': '121.0', 'field_b': 'dasd', 'field_c': None, - 'field_d': datetime.datetime(2011, 12, 25, 12, 45, 00) + 'field_d': '2011-12-25 12:45:00' } self._complex_data_input = StringIO( '' @@ -39,15 +37,15 @@ def setUp(self): '' ) self._complex_data = { - "creation_date": datetime.datetime(2011, 12, 25, 12, 45, 00), + "creation_date": "2011-12-25 12:45:00", "name": "name", "sub_data_list": [ { - "sub_id": 1, + "sub_id": "1", "sub_name": "first" }, { - "sub_id": 2, + "sub_id": "2", "sub_name": "second" } ] diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 4168868..57ecd5c 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -32,6 +32,20 @@ class XMLRendererTestCase(TestCase): } ] } + _complex_data_out = { + "creation_date": "2011-12-25 12:45:00", + "name": "name", + "sub_data_list": [ + { + "sub_id": "1", + "sub_name": "first" + }, + { + "sub_id": "2", + "sub_name": "second" + } + ] + } def test_render_string(self): """ @@ -114,8 +128,9 @@ def test_render_and_parse_complex_data(self): parser = XMLParser() complex_data_out = parser.parse(content) - error_msg = "complex data differs!IN:\n %s \n\n OUT:\n %s" % (repr(self._complex_data), repr(complex_data_out)) - self.assertEqual(self._complex_data, complex_data_out, error_msg) + error_msg = "complex data differs!IN:\n %s \n\n OUT:\n %s" % ( + repr(complex_data_out), repr(self._complex_data_out)) + self.assertEqual(complex_data_out, self._complex_data_out, error_msg) def assertXMLContains(self, xml, string): self.assertTrue(xml.startswith('\n'))