@@ -106,6 +106,10 @@ def encode_sub(self, validator, value):
106106 # serialization
107107 validate_f = validator .validate
108108 encode_f = self .encode_list
109+ elif isinstance (validator , bv .Map ):
110+ # Also validate maps during serialization because they are also mutable
111+ validate_f = validator .validate
112+ encode_f = self .encode_map
109113 elif isinstance (validator , bv .Nullable ):
110114 validate_f = validator .validate
111115 encode_f = self .encode_nullable
@@ -139,6 +143,14 @@ def encode_list(self, validator, value):
139143 """
140144 raise NotImplementedError
141145
146+ def encode_map (self , validator , value ):
147+ # type: (bv.Map, typing.Any) -> typing.Any
148+ """
149+ Callback for serializing a ``stone_validators.Map``. Arguments
150+ have the same semantics as with the ``encode`` method.
151+ """
152+ raise NotImplementedError
153+
142154 def encode_nullable (self , validator , value ):
143155 # type: (bv.Nullable, typing.Any) -> typing.Any
144156 """
@@ -222,6 +234,15 @@ def encode_list(self, validator, value):
222234 return [self .encode_sub (validator .item_validator , value_item ) for value_item in
223235 validated_value ]
224236
237+ def encode_map (self , validator , value ):
238+ validated_value = validator .validate (value )
239+
240+ return {
241+ self .encode_sub (validator .key_validator , key ):
242+ self .encode_sub (validator .value_validator , value ) for
243+ key , value in validated_value .items ()
244+ }
245+
225246 def encode_nullable (self , validator , value ):
226247 if value is None :
227248 return None
@@ -461,6 +482,7 @@ def json_decode(
461482 - Float -> float
462483 - Integer -> long
463484 - List -> list
485+ - Map -> dict
464486 - Nullable -> None or its wrapped type.
465487 - String -> unicode (PY2) or str (PY3)
466488 - Struct -> An instance of its definition attribute.
@@ -522,6 +544,9 @@ def _json_compat_obj_decode_helper(
522544 elif isinstance (data_type , bv .List ):
523545 return _decode_list (
524546 data_type , obj , alias_validators , strict , old_style , for_msgpack )
547+ elif isinstance (data_type , bv .Map ):
548+ return _decode_map (
549+ data_type , obj , alias_validators , strict , old_style , for_msgpack )
525550 elif isinstance (data_type , bv .Nullable ):
526551 return _decode_nullable (
527552 data_type , obj , alias_validators , strict , old_style , for_msgpack )
@@ -656,7 +681,7 @@ def _decode_union_dict(data_type, obj, alias_validators, strict, for_msgpack):
656681 raise bv .ValidationError ("unexpected key '%s'" % key )
657682 val = None
658683 elif isinstance (val_data_type ,
659- (bv .Primitive , bv .List , bv .StructTree , bv .Union )):
684+ (bv .Primitive , bv .List , bv .StructTree , bv .Union , bv . Map )):
660685 if tag in obj :
661686 raw_val = obj [tag ]
662687 try :
@@ -812,6 +837,26 @@ def _decode_list(
812837 for item in obj ]
813838
814839
840+ def _decode_map (
841+ data_type , obj , alias_validators , strict , old_style , for_msgpack ):
842+ """
843+ The data_type argument must be a Map.
844+ See json_compat_obj_decode() for argument descriptions.
845+ """
846+ if not isinstance (obj , dict ):
847+ raise bv .ValidationError (
848+ 'expected dict, got %s' % bv .generic_type_name (obj ))
849+ return {
850+ _json_compat_obj_decode_helper (
851+ data_type .key_validator , key , alias_validators , strict ,
852+ old_style , for_msgpack ):
853+ _json_compat_obj_decode_helper (
854+ data_type .value_validator , value , alias_validators , strict ,
855+ old_style , for_msgpack )
856+ for key , value in obj .items ()
857+ }
858+
859+
815860def _decode_nullable (
816861 data_type , obj , alias_validators , strict , old_style , for_msgpack ):
817862 """
0 commit comments