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_