| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
 | """
Parsers are used to parse the content of incoming HTTP requests.
They give us a generic way of being able to handle various media types
on the request, such as form content or json encoded data.
"""
from django.http import QueryDict
from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser
from django.http.multipartparser import MultiPartParserError
from rest_framework.compat import yaml, ETParseError
from rest_framework.exceptions import ParseError
from xml.etree import ElementTree as ET
from xml.parsers.expat import ExpatError
import json
import datetime
import decimal
class DataAndFiles(object):
    def __init__(self, data, files):
        self.data = data
        self.files = files
class BaseParser(object):
    """
    All parsers should extend `BaseParser`, specifying a `media_type`
    attribute, and overriding the `.parse()` method.
    """
    media_type = None
    def parse(self, stream, media_type=None, parser_context=None):
        """
        Given a stream to read from, return the parsed representation.
        Should return parsed data, or a `DataAndFiles` object consisting of the
        parsed data and files.
        """
        raise NotImplementedError(".parse() must be overridden.")
class JSONParser(BaseParser):
    """
    Parses JSON-serialized data.
    """
    media_type = 'application/json'
    def parse(self, stream, media_type=None, parser_context=None):
        """
        Returns a 2-tuple of `(data, files)`.
        `data` will be an object which is the parsed content of the response.
        `files` will always be `None`.
        """
        try:
            return json.load(stream)
        except ValueError, exc:
            raise ParseError('JSON parse error - %s' % unicode(exc))
class YAMLParser(BaseParser):
    """
    Parses YAML-serialized data.
    """
    media_type = 'application/yaml'
    def parse(self, stream, media_type=None, parser_context=None):
        """
        Returns a 2-tuple of `(data, files)`.
        `data` will be an object which is the parsed content of the response.
        `files` will always be `None`.
        """
        try:
            return yaml.safe_load(stream)
        except (ValueError, yaml.parser.ParserError), exc:
            raise ParseError('YAML parse error - %s' % unicode(exc))
class FormParser(BaseParser):
    """
    Parser for form data.
    """
    media_type = 'application/x-www-form-urlencoded'
    def parse(self, stream, media_type=None, parser_context=None):
        """
        Returns a 2-tuple of `(data, files)`.
        `data` will be a :class:`QueryDict` containing all the form parameters.
        `files` will always be :const:`None`.
        """
        data = QueryDict(stream.read())
        return data
class MultiPartParser(BaseParser):
    """
    Parser for multipart form data, which may include file data.
    """
    media_type = 'multipart/form-data'
    def parse(self, stream, media_type=None, parser_context=None):
        """
        Returns a DataAndFiles object.
        `.data` will be a `QueryDict` containing all the form parameters.
        `.files` will be a `QueryDict` containing all the form files.
        """
        parser_context = parser_context or {}
        request = parser_context['request']
        meta = request.META
        upload_handlers = request.upload_handlers
        try:
            parser = DjangoMultiPartParser(meta, stream, upload_handlers)
            data, files = parser.parse()
            return DataAndFiles(data, files)
        except MultiPartParserError, exc:
            raise ParseError('Multipart form parse error - %s' % unicode(exc))
class XMLParser(BaseParser):
    """
    XML parser.
    """
    media_type = 'application/xml'
    def parse(self, stream, media_type=None, parser_context=None):
        try:
            tree = ET.parse(stream)
        except (ExpatError, ETParseError, ValueError), exc:
            raise ParseError('XML parse error - %s' % unicode(exc))
        data = self._xml_convert(tree.getroot())
        return data
    def _xml_convert(self, element):
        """
        convert the xml `element` into the corresponding python object
        """
        children = element.getchildren()
        if len(children) == 0:
            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":
                data = []
                for child in children:
                    data.append(self._xml_convert(child))
            else:
                data = {}
                for child in children:
                    data[child.tag] = self._xml_convert(child)
            return data
    def _type_convert(self, value):
        """
        Converts the value returned by the XMl parse into the equivalent
        Python type
        """
        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
 |