Mercurial > parpg-core
comparison src/parpg/serializers.py @ 0:1fd2201f5c36
Initial commit of parpg-core.
author | M. George Hansen <technopolitica@gmail.com> |
---|---|
date | Sat, 14 May 2011 01:12:35 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:1fd2201f5c36 |
---|---|
1 # This file is part of PARPG. | |
2 | |
3 # PARPG is free software: you can redistribute it and/or modify | |
4 # it under the terms of the GNU General Public License as published by | |
5 # the Free Software Foundation, either version 3 of the License, or | |
6 # (at your option) any later version. | |
7 | |
8 # PARPG is distributed in the hope that it will be useful, | |
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 # GNU General Public License for more details. | |
12 | |
13 # You should have received a copy of the GNU General Public License | |
14 # along with PARPG. If not, see <http://www.gnu.org/licenses/>. | |
15 """ | |
16 Provides classes used to serialize and deserialize Python classes. | |
17 """ | |
18 | |
19 from abc import ABCMeta, abstractmethod | |
20 try: | |
21 from xml.etree import cElementTree as ElementTree | |
22 except ImportError: | |
23 from xml.etree import ElementTree | |
24 try: | |
25 from collections import OrderedDict | |
26 except ImportError: | |
27 from .common.ordereddict import OrderedDict | |
28 | |
29 from .common.utils import dedent_chomp | |
30 | |
31 import logging | |
32 | |
33 logger = logging.getLogger('serializers') | |
34 | |
35 class Serializable(object): | |
36 def __init__(self, class_, init_args=None, attributes=None): | |
37 self.class_ = class_ | |
38 if init_args is not None: | |
39 self.init_args = OrderedDict(init_args) | |
40 else: | |
41 self.init_args = OrderedDict() | |
42 if attributes is not None: | |
43 self.attributes = OrderedDict(attributes) | |
44 else: | |
45 self.attributes = OrderedDict() | |
46 | |
47 | |
48 class SerializableRegistry(object): | |
49 """ | |
50 Class holding the data used to serialize and deserialize a particular | |
51 Python object. | |
52 """ | |
53 registered_classes = {} | |
54 | |
55 @classmethod | |
56 def registerClass(cls, name, class_, init_args=None, attributes=None): | |
57 serializable = Serializable(class_, init_args, attributes) | |
58 cls.registered_classes[name] = serializable | |
59 | |
60 | |
61 class AbstractSerializer(object): | |
62 __metaclass__ = ABCMeta | |
63 | |
64 @abstractmethod | |
65 def serialize(self, object_, stream): | |
66 pass | |
67 | |
68 @abstractmethod | |
69 def deserialize(self, stream): | |
70 pass | |
71 | |
72 | |
73 class XmlSerializer(AbstractSerializer): | |
74 def serialize(self, statistic, stream): | |
75 pass | |
76 | |
77 @classmethod | |
78 def deserialize(cls, stream): | |
79 element_tree = ElementTree.parse(stream) | |
80 root_element = element_tree.getroot() | |
81 object_ = cls.construct_object(root_element) | |
82 return object_ | |
83 | |
84 @classmethod | |
85 def construct_object(cls, element): | |
86 element_name = element.tag | |
87 if element_name in SerializableRegistry.registered_classes.keys(): | |
88 object_ = cls.construct_registered_class(element) | |
89 elif len(element) > 0: | |
90 # Element contains subelements, so we'll treat it as an | |
91 # OrderedDict. | |
92 if element_name == 'list': | |
93 object_ = cls.construct_list(element) | |
94 else: | |
95 object_ = cls.construct_ordered_dict(element) | |
96 else: | |
97 object_ = cls.construct_primitive(element) | |
98 return object_ | |
99 | |
100 @classmethod | |
101 def construct_registered_class(cls, element): | |
102 element_name = element.tag | |
103 serializable = SerializableRegistry.registered_classes[element_name] | |
104 class_ = serializable.class_ | |
105 init_args = OrderedDict() | |
106 for subelement in element: | |
107 arg = cls.construct_object(subelement) | |
108 subelement_name = subelement.tag | |
109 init_args[subelement_name] = arg | |
110 try: | |
111 object_ = class_(**init_args) | |
112 except (TypeError, ValueError) as exception: | |
113 logger.error(init_args) | |
114 error_message = \ | |
115 'unable to deserialize tag {0}: {1}'.format(element_name, | |
116 exception) | |
117 raise ValueError(error_message) | |
118 return object_ | |
119 | |
120 @classmethod | |
121 def construct_ordered_dict(cls, element): | |
122 object_ = OrderedDict() | |
123 for subelement in element: | |
124 child = cls.construct_object(subelement) | |
125 name = subelement.tag | |
126 object_[name] = child | |
127 return object_ | |
128 | |
129 @classmethod | |
130 def construct_list(cls, element): | |
131 object_ = [] | |
132 for subelement in element: | |
133 child = cls.construct_object(subelement) | |
134 object_.append(child) | |
135 return object_ | |
136 | |
137 @classmethod | |
138 def construct_primitive(cls, element): | |
139 text = element.text | |
140 # Interpret the element's text as unicode by default. | |
141 element_type = element.attrib.get('type', 'unicode') | |
142 if element_type == 'unicode': | |
143 formatted_text = dedent_chomp(text) | |
144 object_ = unicode(formatted_text) | |
145 elif element_type == 'str': | |
146 formatted_text = dedent_chomp(text) | |
147 object_ = str(formatted_text) | |
148 elif element_type == 'int': | |
149 object_ = int(text) | |
150 elif element_type == 'float': | |
151 object_ = float(text) | |
152 else: | |
153 error_message = '{0!r} is not a recognized primitive type' | |
154 error_message.format(element_type) | |
155 raise ValueError(error_message) | |
156 return object_ |