1

我正在使用 Python 测试 REST API。我收到一条 json 格式的响应消息,并想检查每个字段。我创建了一个 check_json 函数并对照检查器字典进行检查。检查器字典有一个字符串键,它是 json 中键的名称,其值为一个带有第一个 bool 参数的元组(一对),项目是否是强制性的,第二个参数是进行直接比较的对象还是添加更多涉及的函数检查功能。

我这样检查:

r= check_json(myjson, checker)

其中 r 是格式的结果:'field':True 或 False - 取决于检查是通过还是失败

代码有点乱,有很多全局函数。一个想法是在 check_json 中包含检查功能。有人告诉我,我也可以使用闭包。但是怎么做?

这是我的代码:

# check nested json
import json
import collections
import functools
import datetime

#this is json from GET session
myjson = {
    "accessed": "Wed, 31 Jul 2013 13:03:38 GMT",
    "created": "Wed, 31 Jul 2013 13:03:38 GMT",
    "dnUrls": [
        "http://135.86.180.69:8580/ucc/api/v1/session/dns/50000"
    ],
    "expires": "Wed, 31 Jul 2013 13:03:48 GMT",
    "modified": "Wed, 31 Jul 2013 13:03:38 GMT",
    "name": "KW50000",
    "person": {
        "employeeId": "KW50000",
        "firstName": "KW50000",
        "lastName": "Dev5"
    },
    "previewRedirect": {
        "destination": "",
        "enabled": False,
        "setupEnabled": False
    }
}

def isDate(s):
    try:
        testdate = datetime.datetime.strptime(s, '%a, %d %b %Y %H:%M:%S GMT')
        return True
    except Exception:
        print "conversion to date failed"
        return False

def isURL(l):
    count = 0
    for item in l:
        count += 1 if item.startswith("http://") else (-1)

    return count > 0

def isAlnum(s):
    return s.isalnum()

def isBool(s):
    return type(s) == bool

def isAny(s):
    return True


#checker object made up of dictionary with string key and tuple value (a pair)
#tuple first filed is flag indicating whether key is mandatory or not.
# tuple 2nd key is value to expect or a checker function
checker = {
    "accessed": (True, isDate),
    "created": (True, isDate),
    "dnUrls": (True, isURL),
    "expires": (True, isDate),
    "modified": (True, isDate),
    "name": (True, "KW50000"),
    "person": (True, {
        "employeeId": (True, isAlnum),
        "firstName": (False, isAny),
        "lastName": (False, isAny)
    }),
    "previewRedirect": (True, {
        "destination": (False, isAny),
        "enabled": (True, isBool),
        "setupEnabled": (False, isBool)
    })
}


# returns dictionary with key= fieldname, value = result, either True (Test Pass), False (test Failed)
def check_json(obj, checker):
    """params json to check, template comparison object
       returns dictionary of keys to pass/fail values"""

    result = {}
    for k, (mFlag, chk) in checker.iteritems():
        if not k in obj:
            result[k] = not mFlag
        elif isinstance(chk, collections.Callable):    
            result[k] = chk(obj[k])
        elif(isinstance(chk, collections.Mapping)):
            result[k] = check_json(obj[k], chk)
        else:       
            result[k] = chk == obj[k]
    return result

def with_and(v1, v2):
    return functools.reduce(with_and, v2.itervalues(), v1) if isinstance(v2, collections.Mapping) else v1 and v2


r= check_json(myjson, checker)
print "r={}".format(r)
isOK = functools.reduce(with_and, r.itervalues(), True)
print "Result is {}: {}".format(isOK, r)
4

1 回答 1

0

这是一种可能的方法。您将不得不决定它是否更具可读性/可维护性/等。

# check nested json
import collections
from functools import reduce
import datetime

#this is json from GET session
myjson = {
    "accessed": "Wed, 31 Jul 2013 13:03:38 GMT",
    "created": "Wed, 31 Jul 2013 13:03:38 GMT",
    "dnUrls": [
        "http://135.86.180.69:8580/ucc/api/v1/session/dns/50000"
    ],
    "expires": "Wed, 31 Jul 2013 13:03:48 GMT",
    "modified": "Wed, 31 Jul 2013 13:03:38 GMT",
    "name": "KW50000",
    "person": {
        "employeeId": "KW50000",
        "firstName": "KW50000",
        "lastName": "Dev5"
    },
    "previewRedirect": {
        "destination": "",
        "enabled": False,
        "setupEnabled": False
    }
}


# returns dictionary with key= fieldname, value = result,
# either True (Test Pass), False (test Failed)
def check_json(obj, checker):
    """params json to check, template comparison object
       returns dictionary of keys to pass/fail values"""

    result = {}
    for k, (mFlag, chk) in checker.items():
        if not k in obj:
            result[k] = not mFlag
        elif isinstance(chk, collections.Callable):
            result[k] = chk(obj[k])
        elif(isinstance(chk, collections.Mapping)):
            result[k] = check_json(obj[k], chk)
        else:
            result[k] = chk == obj[k]
    return result

    def isDate(s):
        try:
            datetime.datetime.strptime(s, '%a, %d %b %Y %H:%M:%S GMT')
            return True
        except Exception:
            print("conversion to date failed")
            return False


#checker object made up of dictionary with string key and tuple value (a pair)
#tuple first filed is flag indicating whether key is mandatory or not.
# tuple 2nd key is value to expect or a checker function
checker = {
    "accessed": (True, check_json.isDate),
    "created": (True, check_json.isDate),
    "dnUrls": (True, lambda l: (reduce(lambda c, v:\
                     c + (1 if v.startswith('http://') else -1), l, 0) > 0)),
    "expires": (True, check_json.isDate),
    "modified": (True, check_json.isDate),
    "name": (True, "KW50000"),
    "person": (True, {
        "employeeId": (True, lambda s: s.isalnum()),
        "firstName": (False, True),
        "lastName": (False, True)
    }),
    "previewRedirect": (True, {
        "destination": (False, True),
        "enabled": (True, lambda s: type(s) is bool),
        "setupEnabled": (False, lambda s: type(s) is bool)
    })
}


def with_and(v1, v2):
    return functools.reduce(with_and, v2.values(), v1)\
                    if isinstance(v2, collections.Mapping) else v1 and v2


r = check_json(myjson, checker)
print("r={}".format(r))
isOK = functools.reduce(with_and, r.values(), True)
print("Result is {}: {}".format(isOK, r))

请注意,代码已针对 Python3 进行了修改。主要的变化是lambdas尽可能使用,否则使用嵌套函数来消除全局命名空间依赖性。

于 2013-07-31T19:43:10.250 回答