55from stream_framework .utils import epoch_to_datetime , datetime_to_epoch
66from stream_framework .serializers .base import BaseAggregatedSerializer
77import six
8+ import re
89
910
1011class AggregatedActivitySerializer (BaseAggregatedSerializer ):
@@ -23,7 +24,6 @@ class AggregatedActivitySerializer(BaseAggregatedSerializer):
2324 #: indicates if dumps returns dehydrated aggregated activities
2425 dehydrate = True
2526 identifier = 'v3'
26- reserved_characters = [';' , ',' , ';;' ]
2727 date_fields = ['created_at' , 'updated_at' , 'seen_at' , 'read_at' ]
2828
2929 activity_serializer_class = ActivitySerializer
@@ -55,7 +55,8 @@ def dumps(self, aggregated):
5555 else :
5656 for activity in aggregated .activities :
5757 serialized = activity_serializer .dumps (activity )
58- check_reserved (serialized , [';' , ';;' ])
58+ # we use semicolons as delimiter, so need to escape
59+ serialized = serialized .replace (";" , "\;" )
5960 serialized_activities .append (serialized )
6061
6162 serialized_activities_part = ';' .join (serialized_activities )
@@ -71,41 +72,42 @@ def dumps(self, aggregated):
7172
7273 def loads (self , serialized_aggregated ):
7374 activity_serializer = self .activity_serializer_class (Activity )
74- try :
75- serialized_aggregated = serialized_aggregated [2 :]
76- parts = serialized_aggregated .split (';;' )
77- # start with the group
78- group = parts [0 ]
79- aggregated = self .aggregated_activity_class (group )
80-
81- # get the date and activities
82- date_dict = dict (zip (self .date_fields , parts [1 :5 ]))
83- for k , v in date_dict .items ():
84- date_value = None
85- if v != '-1' :
86- date_value = epoch_to_datetime (float (v ))
87- setattr (aggregated , k , date_value )
88-
89- # write the activities
90- serializations = parts [5 ].split (';' )
91- if self .dehydrate :
92- activity_ids = list (map (int , serializations ))
93- aggregated ._activity_ids = activity_ids
94- aggregated .dehydrated = True
95- else :
96- activities = [activity_serializer .loads (s )
97- for s in serializations ]
98- aggregated .activities = activities
99- aggregated .dehydrated = False
100-
101- # write the minimized activities
102- minimized = int (parts [6 ])
103- aggregated .minimized_activities = minimized
104-
105- return aggregated
106- except Exception as e :
107- msg = six .text_type (e )
108- raise SerializationException (msg )
75+ serialized_aggregated = serialized_aggregated [2 :]
76+ parts = serialized_aggregated .split (';;' )
77+ # start with the group
78+ group = parts [0 ]
79+ aggregated = self .aggregated_activity_class (group )
80+
81+ # get the date and activities
82+ date_dict = dict (zip (self .date_fields , parts [1 :5 ]))
83+ for k , v in date_dict .items ():
84+ date_value = None
85+ if v != '-1' :
86+ date_value = epoch_to_datetime (float (v ))
87+ setattr (aggregated , k , date_value )
88+
89+ # looks for ; not \;
90+ unescaped_semicolons_regex = re .compile ("(?<=[^\\ \]);" )
91+ # write the activities
92+ serializations = unescaped_semicolons_regex .split (parts [5 ])
93+ if self .dehydrate :
94+ activity_ids = list (map (int , serializations ))
95+ aggregated ._activity_ids = activity_ids
96+ aggregated .dehydrated = True
97+ else :
98+ activities = []
99+ for s in serializations :
100+ s = s .replace ("\;" , ";" )
101+ deserialized = activity_serializer .loads (s )
102+ activities .append (deserialized )
103+ aggregated .activities = activities
104+ aggregated .dehydrated = False
105+
106+ # write the minimized activities
107+ minimized = int (parts [6 ])
108+ aggregated .minimized_activities = minimized
109+
110+ return aggregated
109111
110112
111113class NotificationSerializer (AggregatedActivitySerializer ):
0 commit comments