给定一个 Cerberus JSON 模式文件,我如何生成一个字典对象

Given a Cerberus JSON Schema file, how can I generate a dictionary object

问题

我有一个 Cerberus Json 模式文件,我需要根据给定 data_types 的默认值模式生成 Json 文件。字典对象中的键 _type 是要检查的 data_type。

Cerberus 示例 Json 模式

{
    "schema": {
        "vars": {
            "forest_config": {
                "_type": "list",
                "_required": false,
                "_not_empty": false,
                "_item_schema": {
                    "_type": "dict",
                    "always_create_dl_here": {
                        "_required": false,
                        "_type": "bool"
                    },
                    "ldap_domain": {
                        "_type": "string",
                        "_required": false
                    },
                    "dc_dn": {
                        "_type": "string",
                        "_required": true,
                        "_not_empty": true,
                        "_regex": "^(DC|O)=[\w\-\. &]+(,(DC|O)=[\w\-\. &]+)*$"
                    },
                    "user_ou": {
                        "_type": "string",
                        "_required": true,
                        "_not_empty": false,
                        "_regex": "^((CN|OU)=[\w\-\. &]+(,(CN|OU)=[\w\-\. &]+)*)?$"
                    },
                    "group_ou": {
                        "_type": "string",
                        "_required": true,
                        "_not_empty": false,
                        "_regex": "^((CN|OU)=[\w\-\. &]+(,(CN|OU)=[\w\-\. &]+)*)?$"
                    },
                    "dl_group_ou": {
                        "_type": "string",
                        "_required": true,
                        "_not_empty": false,
                        "_regex": "^((CN|OU)=[\w\-\. &]+(,(CN|OU)=[\w\-\. &]+)*)?$"
                    }
                }
            }
        }
    }
}

我尝试制作我自己的递归函数,但是我卡住了

        new_obj = {}
        def recursively_generate_schema_obj(dict_obj: Dict):
            if isinstance(dict_obj, dict):
                var_name = None
                for key, value in dict_obj.items():
                    if not str(key).startswith("_"):
                        var_name = key

                    if var_name and '_type' not in dict_obj:
                        new_obj[var_name] = {}
                    elif var_name and '_type' in dict_obj and dict_obj['_type'] == 'list':
                        new_obj[var_name] = [recursively_generate_schema_obj(dict_obj)]
                    else:
                        new_obj[var_name] = create_default_type_value(dict_obj['_type'])

                    recursively_generate_schema_obj(value)

        recursively_generate_schema_obj(schema_data)


    def get_default_value(data_type:str):
        if data_type == 'string':
            return ''
        elif data_type == 'dict':
            return {}
        elif data_type == 'bool':
            return False
        elif data_type == 'list':
            return []
        elif data_type == 'int':
            return 0
        elif data_type == 'enum': # this needs to be handled in the calling function
            return ''

我的代码的当前输出

{'schema': {}, 'vars': {}, 'forest_config': {}, None: '', 'always_create_dl_here': {}, 'ldap_domain': {}, 'dc_dn': {}, 'user_ou': {}, 'group_ou': {}, 'dl_group_ou': {}}

这绝对是错误的

应该是

{
    "schema": {
        "vars": {
            "forest_config": [
                {
                    "always_create_dl_here": false,
                    "ldap_domain": "",
                    "dc_dn": "",
                    "user_ou": "",
                    "group_ou": "",
                    "dl_group_ou": ""
                }
            ]
        }
    }
}

询问

有人可以帮我解决这个问题并解释一些可能的更好方法吗?

您已经非常接近可行的东西了。我修改了函数以使用 var_namejson_obj 以及 var_schema。然后它可以使用 var_schema 中的 _type 添加 var_name 到提供的 json_obj。如果在 var_schema 中定义了更多的变量,那么它会调用添加每个变量的函数。传递该变量的架构详细信息以及要将变量添加到的 json_obj

我不熟悉你提到的 Cerberus Json 模式,所以你可能想在 get_default_value 函数中添加一些额外的代码。希望您的 enum 类型的详细信息在架构中。

def get_default_value(var_schema: dict):
    data_type = var_schema.get('_type', 'dict')
    if data_type == 'string':
        return ''
        # return var_schema.get('_regex', '')  # use this if you want to get the regex as the default value
    elif data_type == 'dict':
        return {}
    elif data_type == 'bool':
        return False
    elif data_type == 'list':
        return []
    elif data_type == 'int':
        return 0
    elif data_type == 'enum':  # this needs extra handling?
        return ''


def recursively_generate_schema_obj(var_schema: dict, var_name=None, json_obj=None):
    """ Add the supplied var_name to the supplied json_obj. The type of variable added is determined by the '_type'
    field in the supplied var_schema.

    Once the variable has been added, find variables in the supplied schema and recursively call this function to
    add any found.

    :param var_name: Name of variable to add to supplied json_obj
    :param var_schema: Schema with details of variables to add below this variable
    :param json_obj: The json object to add the variable to. This can be a dict or a list. If it's a list, then the
    supplied var_name is not used and an item of type defined in the var_schema is simply appended to the list
    :return: The json_obj
    """
    if isinstance(var_schema, dict):

        next_level = get_default_value(var_schema)
        if json_obj is None:
            json_obj = next_level
        elif isinstance(json_obj, list):
            json_obj.append(next_level)
        else:
            json_obj[var_name] = next_level

        # Find each sub field in the schema and add it to the next level
        # These are keys without an '_' or, for the items to add to a list the '_item_schema'.
        for key, value in var_schema.items():
            if not str(key).startswith("_") or key == '_item_schema':
                recursively_generate_schema_obj(value, key, next_level)

    return json_obj


schema_data = {
    'schema': {
        'vars': {
            'forest_config': {
                '_type': 'list',
                '_required': False,
                '_not_empty': False,
                '_item_schema': {
                    '_type': 'dict',
                    'always_create_dl_here': {'_required': False, '_type': 'bool'},
                    'ldap_domain': {'_type': 'string', '_required': False},
                    'dc_dn': {'_type': 'string', '_required': True, '_not_empty': True,
                              '_regex': '^(DC|O)=[\w\-\. &]+(,(DC|O)=[\w\-\. &]+)*$'},
                    'user_ou': {'_type': 'string','_required': True,'_not_empty': False,
                                '_regex': '^((CN|OU)=[\w\-\. &]+(,(CN|OU)=[\w\-\. &]+)*)?$'},
                    'group_ou': {'_type': 'string', '_required': True, '_not_empty': False,
                                 '_regex': '^((CN|OU)=[\w\-\. &]+(,(CN|OU)=[\w\-\. &]+)*)?$'},
                    'dl_group_ou': {'_type': 'string', '_required': True, '_not_empty': False,
                                    '_regex': '^((CN|OU)=[\w\-\. &]+(,(CN|OU)=[\w\-\. &]+)*)?$'}
                }
            }
        }
    }
}

new_obj = recursively_generate_schema_obj(schema_data)
print(new_obj)

以上代码产生:

{
    'schema': {
        'vars': {
            'forest_config': [{
                'always_create_dl_here': False, 
                'ldap_domain': '', 
                'dc_dn': '', 
                'user_ou': '', 
                'group_ou': '', 
                'dl_group_ou': ''
            }]
        }
    }
}