1 """JSON utility functions."""
3 from __future__
import annotations
6 from os
import PathLike
14 _LOGGER = logging.getLogger(__name__)
16 type JsonValueType = (
17 dict[str, JsonValueType] | list[JsonValueType] | str | int | float | bool |
None
19 """Any data that can be returned by the standard JSON deserializing process."""
20 type JsonArrayType = list[JsonValueType]
21 """List that can be returned by the standard JSON deserializing process."""
22 type JsonObjectType = dict[str, JsonValueType]
23 """Dictionary that can be returned by the standard JSON deserializing process."""
25 JSON_ENCODE_EXCEPTIONS = (TypeError, ValueError)
26 JSON_DECODE_EXCEPTIONS = (orjson.JSONDecodeError,)
30 """Error serializing the data to JSON."""
33 def json_loads(obj: bytes | bytearray | memoryview | str, /) -> JsonValueType:
36 This adds a workaround for orjson not handling subclasses of str,
37 https://github.com/ijl/orjson/issues/445.
40 if type(obj)
not in (bytes, bytearray, memoryview, str)
and isinstance(obj, str):
41 return orjson.loads(
str(obj))
42 return orjson.loads(obj)
46 """Parse JSON data and ensure result is a list."""
49 if type(value)
is list:
51 raise ValueError(f
"Expected JSON to be parsed as a list got {type(value)}")
55 """Parse JSON data and ensure result is a dictionary."""
58 if type(value)
is dict:
60 raise ValueError(f
"Expected JSON to be parsed as a dict got {type(value)}")
64 filename: str | PathLike[str],
65 default: JsonValueType = _SENTINEL,
67 """Load JSON data from a file.
69 Defaults to returning empty dict if file is not found.
72 with open(filename, mode=
"rb")
as fdesc:
73 return orjson.loads(fdesc.read())
74 except FileNotFoundError:
76 _LOGGER.debug(
"JSON file not found: %s", filename)
77 except JSON_DECODE_EXCEPTIONS
as error:
78 _LOGGER.exception(
"Could not parse JSON content: %s", filename)
80 except OSError
as error:
81 _LOGGER.exception(
"JSON file reading failed: %s", filename)
83 return {}
if default
is _SENTINEL
else default
87 filename: str | PathLike[str],
88 default: JsonArrayType = _SENTINEL,
90 """Load JSON data from a file and return as list.
92 Defaults to returning empty list if file is not found.
94 if default
is _SENTINEL:
96 value: JsonValueType =
load_json(filename, default=default)
98 if type(value)
is list:
101 "Expected JSON to be parsed as a list got %s in: %s", {type(value)}, filename
107 filename: str | PathLike[str],
108 default: JsonObjectType = _SENTINEL,
110 """Load JSON data from a file and return as dict.
112 Defaults to returning empty dict if file is not found.
114 if default
is _SENTINEL:
116 value: JsonValueType =
load_json(filename, default=default)
118 if type(value)
is dict:
121 "Expected JSON to be parsed as a dict got %s in: %s", {type(value)}, filename
127 """Format output of find_paths in a friendly way.
129 Format is comma separated: <path>=<value>(<type>)
131 return ", ".join(f
"{path}={value}({type(value)}" for path, value
in data.items())
None open(self, **Any kwargs)
JsonObjectType load_json_object(str|PathLike[str] filename, JsonObjectType default=_SENTINEL)
JsonArrayType load_json_array(str|PathLike[str] filename, JsonArrayType default=_SENTINEL)
JsonArrayType json_loads_array(bytes|bytearray|memoryview|str obj)
JsonObjectType json_loads_object(bytes|bytearray|memoryview|str obj)
JsonValueType json_loads(bytes|bytearray|memoryview|str obj)
JsonValueType load_json(str|PathLike[str] filename, JsonValueType default=_SENTINEL)
str format_unserializable_data(dict[str, Any] data)