source: branches/active/character_customization/game/parpg/serializers.py @ 766

Revision 736, 4.4 KB checked in by aspidites, 8 years ago (diff)

Patch by Aspidites:

  • renamed scripts package to parpg
  • renamed parpg module to application
  • removed packaging and other related files (kept locally for reference, will reintroduce similar scripts to resolve bug #275
  • updated all import statements to respect changes above
Line 
1"""
2Provides classes used to serialize and deserialize Python classes.
3"""
4
5from abc import ABCMeta, abstractmethod
6try:
7    from xml.etree import cElementTree as ElementTree
8except ImportError:
9    from xml.etree import ElementTree
10try:
11    from collections import OrderedDict
12except ImportError:
13    from .common.ordereddict import OrderedDict
14
15from .common.utils import dedent_chomp
16
17class Serializable(object):
18    def __init__(self, class_, init_args=None, attributes=None):
19        self.class_ = class_
20        if init_args is not None:
21            self.init_args = OrderedDict(init_args)
22        else:
23            self.init_args = OrderedDict()
24        if attributes is not None:
25            self.attributes = OrderedDict(attributes)
26        else:
27            self.attributes = OrderedDict()
28
29
30class SerializableRegistry(object):
31    """
32    Class holding the data used to serialize and deserialize a particular
33    Python object.
34    """
35    registered_classes = {}
36   
37    @classmethod
38    def registerClass(cls, name, class_, init_args=None, attributes=None):
39        serializable = Serializable(class_, init_args, attributes)
40        cls.registered_classes[name] = serializable
41
42
43class AbstractSerializer(object):
44    __metaclass__ = ABCMeta
45   
46    @abstractmethod
47    def serialize(self, object_, stream):
48        pass
49   
50    @abstractmethod
51    def deserialize(self, stream):
52        pass
53
54
55class XmlSerializer(AbstractSerializer):
56    def serialize(self, statistic, stream):
57        pass
58   
59    @classmethod
60    def deserialize(cls, stream):
61        element_tree = ElementTree.parse(stream)
62        root_element = element_tree.getroot()
63        object_ = cls.construct_object(root_element)
64        return object_
65   
66    @classmethod
67    def construct_object(cls, element):
68        element_name = element.tag
69        if element_name in SerializableRegistry.registered_classes.keys():
70            object_ = cls.construct_registered_class(element)
71        elif len(element) > 0:
72            # Element contains subelements, so we'll treat it as an
73            # OrderedDict.
74            if element_name == 'list':
75                object_ = cls.construct_list(element)
76            else:
77                object_ = cls.construct_ordered_dict(element)
78        else:
79            object_ = cls.construct_primitive(element)
80        return object_
81   
82    @classmethod
83    def construct_registered_class(cls, element):
84        element_name = element.tag
85        serializable = SerializableRegistry.registered_classes[element_name]
86        class_ = serializable.class_
87        init_args = OrderedDict()
88        for subelement in element:
89            arg = cls.construct_object(subelement)
90            subelement_name = subelement.tag
91            init_args[subelement_name] = arg
92        try:
93            object_ = class_(**init_args)
94        except (TypeError, ValueError) as exception:
95            print(init_args)
96            error_message = \
97                'unable to deserialize tag {0}: {1}'.format(element_name,
98                                                            exception)
99            raise ValueError(error_message)
100        return object_
101   
102    @classmethod
103    def construct_ordered_dict(cls, element):
104        object_ = OrderedDict()
105        for subelement in element:
106            child = cls.construct_object(subelement)
107            name = subelement.tag
108            object_[name] = child
109        return object_
110   
111    @classmethod
112    def construct_list(cls, element):
113        object_ = []
114        for subelement in element:
115            child = cls.construct_object(subelement)
116            object_.append(child)
117        return object_
118   
119    @classmethod
120    def construct_primitive(cls, element):
121        text = element.text
122        # Interpret the element's text as unicode by default.
123        element_type = element.attrib.get('type', 'unicode')
124        if element_type == 'unicode':
125            formatted_text = dedent_chomp(text)
126            object_ = unicode(formatted_text)
127        elif element_type == 'str':
128            formatted_text = dedent_chomp(text)
129            object_ = str(formatted_text)
130        elif element_type == 'int':
131            object_ = int(text)
132        elif element_type == 'float':
133            object_ = float(text)
134        else:
135            error_message = '{0!r} is not a recognized primitive type'
136            error_message.format(element_type)
137            raise ValueError(error_message)
138        return object_
Note: See TracBrowser for help on using the repository browser.