pythonjsoncolander

colander schema for mapping where keys are variable but value are arrays


How do I define the schema in colander for JSON of the following form?

{
    'data' : {
        'key_1' : [123, 567],
        'key_2' : ['abc','def'],
        'frank_underwood' : [666.66, 333.333],
        ... etc ...
    }
}

The keys inside 'data' could be any string and values are arrays.

Currently, I have the following but it doesn't really put any constraints on the types of values the mapping can have.

class Query(colander.MappingSchema):
    data = colander.SchemaNode(
        colander.Mapping(unknown='preserve'),
        missing={}
    )

What's the proper way of describing this?


Solution

  • A possible solution is to use a custom validator.

    Here is a full working example of a custom validator that checks if all values of an arbitrary map are singularly typed arrays.

    import colander
    
    
    def values_are_singularly_typed_arrays(node, mapping):
        for val in mapping.values():
            if not isinstance(val, list):
                raise colander.Invalid(node, "one or more value(s) is not a list")
            if not len(set(map(type, val))) == 1:
                raise colander.Invalid(node, "one or more value(s) is a list with mixed types")
    
    class MySchema(colander.MappingSchema):
        data = colander.SchemaNode(
            colander.Mapping(unknown='preserve'),
            validator=values_are_singularly_typed_arrays
        )
    
    def main():
        valid_data = {
            'data' : {
                'numbers' : [1,2,3],
                'reals' : [1.2,3.4,5.6],
            }
        }
        not_list = {
            'data' : {
                'numbers' : [1,2,3],
                'error_here' : 123
            }
        }
        mixed_type = {
            'data' : {
                'numbers' : [1,2,3],
                'error_here' : [123, 'for the watch']
            }
        }
    
        schema = MySchema()
        schema.deserialize(valid_data)
    
        try:
            schema.deserialize(not_list)
        except colander.Invalid as e:
            print(e.asdict())
    
        try:
            schema.deserialize(mixed_type)
        except colander.Invalid as e:
            print(e.asdict())
    
    if __name__ == '__main__':
        main()