1""" 2Functions to work with JSON 3""" 4 5 6import json 7import logging 8 9import salt.utils.data 10import salt.utils.stringutils 11 12log = logging.getLogger(__name__) 13 14 15# One to one mappings 16JSONEncoder = json.JSONEncoder 17 18 19def __split(raw): 20 """ 21 Performs a splitlines on the string. This function exists to make mocking 22 possible in unit tests, since the member functions of the str/unicode 23 builtins cannot be mocked. 24 """ 25 return raw.splitlines() 26 27 28def find_json(raw): 29 """ 30 Pass in a raw string and load the json when it starts. This allows for a 31 string to start with garbage and end with json but be cleanly loaded 32 """ 33 ret = {} 34 lines = __split(raw) 35 for ind, _ in enumerate(lines): 36 try: 37 working = "\n".join(lines[ind:]) 38 except UnicodeDecodeError: 39 working = "\n".join(salt.utils.data.decode(lines[ind:])) 40 41 try: 42 ret = json.loads(working) 43 except ValueError: 44 continue 45 if ret: 46 return ret 47 if not ret: 48 # Not json, raise an error 49 raise ValueError 50 51 52def import_json(): 53 """ 54 Import a json module, starting with the quick ones and going down the list) 55 """ 56 for fast_json in ("ujson", "yajl", "json"): 57 try: 58 mod = __import__(fast_json) 59 log.trace("loaded %s json lib", fast_json) 60 return mod 61 except ImportError: 62 continue 63 64 65def load(fp, **kwargs): 66 """ 67 .. versionadded:: 2018.3.0 68 69 Wraps json.load 70 71 You can pass an alternate json module (loaded via import_json() above) 72 using the _json_module argument) 73 """ 74 return kwargs.pop("_json_module", json).load(fp, **kwargs) 75 76 77def loads(s, **kwargs): 78 """ 79 .. versionadded:: 2018.3.0 80 81 Wraps json.loads and prevents a traceback in the event that a bytestring is 82 passed to the function. (Python < 3.6 cannot load bytestrings) 83 84 You can pass an alternate json module (loaded via import_json() above) 85 using the _json_module argument) 86 """ 87 json_module = kwargs.pop("_json_module", json) 88 try: 89 return json_module.loads(s, **kwargs) 90 except TypeError as exc: 91 # json.loads cannot load bytestrings in Python < 3.6 92 if isinstance(s, bytes): 93 return json_module.loads(salt.utils.stringutils.to_unicode(s), **kwargs) 94 else: 95 raise 96 97 98def dump(obj, fp, **kwargs): 99 """ 100 .. versionadded:: 2018.3.0 101 102 Wraps json.dump, and assumes that ensure_ascii is False (unless explicitly 103 passed as True) for unicode compatibility. Note that setting it to True 104 will mess up any unicode characters, as they will be dumped as the string 105 literal version of the unicode code point. 106 107 On Python 2, encodes the result to a str since json.dump does not want 108 unicode types. 109 110 You can pass an alternate json module (loaded via import_json() above) 111 using the _json_module argument) 112 """ 113 json_module = kwargs.pop("_json_module", json) 114 if "ensure_ascii" not in kwargs: 115 kwargs["ensure_ascii"] = False 116 return json_module.dump(obj, fp, **kwargs) 117 118 119def dumps(obj, **kwargs): 120 """ 121 .. versionadded:: 2018.3.0 122 123 Wraps json.dumps, and assumes that ensure_ascii is False (unless explicitly 124 passed as True) for unicode compatibility. Note that setting it to True 125 will mess up any unicode characters, as they will be dumped as the string 126 literal version of the unicode code point. 127 128 On Python 2, encodes the result to a str since json.dumps does not want 129 unicode types. 130 131 You can pass an alternate json module (loaded via import_json() above) 132 using the _json_module argument) 133 """ 134 json_module = kwargs.pop("_json_module", json) 135 if "ensure_ascii" not in kwargs: 136 kwargs["ensure_ascii"] = False 137 return json_module.dumps(obj, **kwargs) 138