def json_flatten(a, prefix=''): """Flatten a JSON structure into a dict with str paths as keys. For example, `json_flatten(json.loads('{"a": {"b": "v"}}'))` will return `{'.a.b': u'v'}`; `json_flatten(json.loads('{"a": [1, 2, 3]}'))` returns `{'.a[0]': 1, '.a[2]': 3, '.a[1]': 2}`, etc. """ def add_flat(dict_, key, elem): """If `elem` is itself a dict, merge it with `dict_`. Otherwise, store it in `dict_` under `key`. """ if isinstance(elem, dict): dict_.update(elem) else: dict_[key] = elem res = {} if isinstance(a, list): for n, elem in enumerate(a): add_flat(res, prefix, json_flatten(elem, \ prefix + "[{0}]".format(n))) elif isinstance(a, dict): for key in a.keys(): new_prefix = prefix # Use a different syntax for keys with spaces. if ' ' in key: new_prefix += "['{0}']".format(key) else: new_prefix += ".{0}".format(key) add_flat(res, prefix, json_flatten(a[key], new_prefix)) # If a is not processable by json_flatten (e.g., it's a str) then store # it in res. However, at the top level we don't want to store such an a # as {'': a}. We also don't store None in res; we return it instead. elif a is not None and prefix != '': res[prefix] = a else: res = a return res
def poll_loop(interval, req, date=True, initial_values=True): """Perform requests for JSON data. Print out changes when they occur.""" prev_output = None output = None try: output = req.perform() if initial_values: json_print(output) output = json_flatten(output) except (subprocess.CalledProcessError, urllib.error.HTTPError, ValueError) as e: print(traceback.format_exc(), file=sys.stderr) while True: try: time.sleep(interval) try: prev_output, output = output, json_flatten(req.perform()) diff = json_flat_diff(prev_output, output) if diff is not None: msg = json_diff_str(diff) msg.sort() # If msg is multi-line print each difference on a new line # with indentation. prefix = '' if date: prefix += datetime.datetime.now().isoformat() if len(msg) > 1: print(prefix, \ "\n ", "\n ".join(msg)) else: print(prefix, msg[0]) except (subprocess.CalledProcessError, urllib.HTTPError, ValueError) as e: print(traceback.format_exc(), file=sys.stderr) except KeyboardInterrupt: sys.exit(0)