問題描述

  1. 驗證 JSON

  2. 手動完成驗證 JSON

  3. 資料

  4. function

    def validate(data, template):
        # implement
        # and return True/False
        # in the case of False, return a string describing 
        # the first error encountered
        # in the case of True, string can be empty
        return state, error
    
  5. result

    That should return this:

  6. 更好的做法 ⇒ 製作成 exception 取代 return codes and strings

思考流程

  1. 寫一個遞迴函數
  2. 一個紀錄路徑的變數
  3. 檢查同一個 level 中,template 內必要的 key 是否存在於 data
  4. value type 是 dictionary 則使用遞迴函數
  5. value type 是值,則檢查類別是否符合

1. 判斷 key 是否存在

def match_keys(data, valid, path):
    data_keys = data.keys()
    valid_keys = valid.keys()

    extra_keys = data_keys - valid_keys           # 
    missing_keys = valid_keys - data_keys

    if extra_keys or missing_keys:
        is_ok = False
        missing_msg = ('missing keys:'+','.join({path+"."+str(key) for key in missing_keys})) if missing_keys else ''
        extra_msg = ('extra keys:'+','.join({path+"."+str(key) for key in extra_keys})) if extra_keys else ''
        return False, ' '.join((missing_msg, extra_msg))
    else:
        return True, None

2. 判斷 type 是否符合

def match_types(data, template, path):
    for key, value in template.items():
        if isinstance(value,dict):
            template_type = dict
        else:
            template_type = value
        data_value = data.get(key, object())
        if not isinstance(data_value, template_type):
            err_msg = ('incorrect type: ' + path + '.' + key +' -> expected ' + template_type.__name__ + ', found ' + type(data_value).__name__)
            return False, err_msg

    return True, None

3. 合併 並處理 遞迴

def recurse_validate(data, template, path):
    # key match
    is_ok, err_msg = match_keys(data, template, path)
    if not is_ok:
        return False, err_msg

    # data type
    is_ok, err_msg = match_types(data, template, path)
    if not is_ok:
        return False, err_msg

    # nested dictionaries
    dictionary_type_keys = {key for key, value in template.items() if isinstance(value, dict)}
    for key in dictionary_type_keys:
        sub_path = path + '.' + str(key)
        sub_template = template[key]
        sub_data = data[key]
        is_ok, err_msg = recurse_validate(sub_data, sub_template, sub_path)  
        if not is_ok:
            return False, err_msg

    return True, None