import json from jsonpath_rw import parse class SafeDictSetter(object): """ Specialized write-only dictionary wrapper class that allows for setting nested keys via a path syntax. Example: sds = SafeDictSetter() sds['foo.bar.baz'] = 'hello' # Sets 'foo' = {'bar': {'baz': 'hello'}} sds['somekey'] = None # Does not set the key since the value is None """ def __init__(self, initial_object=None): self._object = initial_object or {} def __setitem__(self, path, value): self.set(path, value) def set(self, path, value, allow_none=False): """ Sets the value of the given path to the given value. """ if value is None and not allow_none: return pieces = path.split('.') current = self._object for piece in pieces[:len(pieces)-1]: current_obj = current.get(piece, {}) if not isinstance(current_obj, dict): raise Exception('Key %s is a non-object value: %s' % (piece, current_obj)) current[piece] = current_obj current = current_obj current[pieces[-1]] = value def dict_value(self): """ Returns the dict value built. """ return self._object def json_value(self): """ Returns the JSON string value of the dictionary built. """ return json.dumps(self._object) class JSONPathDict(object): """ Specialized read-only dictionary wrapper class that uses the jsonpath_rw library to access keys via an X-Path-like syntax. Example: pd = JSONPathDict({'hello': {'hi': 'there'}}) pd['hello.hi'] # Returns 'there' """ def __init__(self, dict_value): """ Init the helper with the JSON object. """ self._object = dict_value def __getitem__(self, path): return self.get(path) def get(self, path, not_found_handler=None): """ Returns the value found at the given path. Path is a json-path expression. """ jsonpath_expr = parse(path) matches = jsonpath_expr.find(self._object) if not matches: return not_found_handler() if not_found_handler else None match = matches[0].value if not match: return not_found_handler() if not_found_handler else None if isinstance(match, dict): return JSONPathDict(match) return match