Coverage for gws-app/gws/lib/jsonx/__init__.py: 82%

38 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-05 13:35 +0200

1"""JSON utilities. 

2 

3Thin wrappers around the standard ``json`` module that read and write JSON strings and 

4files. Objects that are not JSON serializable are converted to their attribute dicts 

5(``vars``) or to strings, so that ``gws.Data`` objects can be serialized directly. 

6All errors are raised as ``Error``. 

7 

8Example:: 

9 

10 s = gws.lib.jsonx.to_pretty_string({'a': 1, 'b': [1, 2]}) 

11 d = gws.lib.jsonx.from_string(s) 

12 gws.lib.jsonx.to_path('/tmp/data.json', d) 

13""" 

14 

15import json 

16 

17import gws 

18 

19 

20class Error(gws.Error): 

21 """JSON error.""" 

22 

23 pass 

24 

25 

26def from_path(path: str): 

27 """Read a JSON file. 

28 

29 Args: 

30 path: Path to a UTF-8 encoded JSON file. 

31 

32 Returns: 

33 The decoded object. 

34 

35 Raises: 

36 ``Error``: If the file cannot be read or is not valid JSON. 

37 """ 

38 

39 try: 

40 with open(path, 'rb') as fp: 

41 s = fp.read() 

42 return json.loads(s.decode('utf8')) 

43 except Exception as exc: 

44 raise Error() from exc 

45 

46 

47def from_string(s: str): 

48 """Decode a JSON string. 

49 

50 Args: 

51 s: JSON string. 

52 

53 Returns: 

54 The decoded object, or an empty dict if the string is empty or blank. 

55 

56 Raises: 

57 ``Error``: If the string is not valid JSON. 

58 """ 

59 

60 if not s.strip(): 

61 return {} 

62 try: 

63 return json.loads(s) 

64 except Exception as exc: 

65 raise Error() from exc 

66 

67 

68def to_path(path: str, x, pretty: bool = False, ensure_ascii: bool = True, default=None): 

69 """Write an object to a JSON file, UTF-8 encoded. 

70 

71 Args: 

72 path: File path. 

73 x: Object to write. 

74 pretty: If ``True``, sort the keys and indent the output. 

75 ensure_ascii: If ``True``, escape non-ASCII characters. 

76 default: Function that returns a serializable version of an object that is not serializable otherwise. 

77 By default, objects are converted to their ``vars``, or to strings if they have none. 

78 

79 Raises: 

80 ``Error``: If the object cannot be encoded or the file cannot be written. 

81 """ 

82 

83 s = to_string(x, pretty=pretty, ensure_ascii=ensure_ascii, default=default) 

84 try: 

85 gws.u.write_file_b(path, s.encode('utf8')) 

86 except Exception as exc: 

87 raise Error() from exc 

88 

89 

90def to_string(x, pretty: bool = False, ensure_ascii: bool = True, default=None) -> str: 

91 """Encode an object as a JSON string. 

92 

93 Args: 

94 x: Object to encode. 

95 pretty: If ``True``, sort the keys and indent the output. 

96 ensure_ascii: If ``True``, escape non-ASCII characters. 

97 default: Function that returns a serializable version of an object that is not serializable otherwise. 

98 By default, objects are converted to their ``vars``, or to strings if they have none. 

99 

100 Returns: 

101 The JSON string. 

102 

103 Raises: 

104 ``Error``: If the object cannot be encoded. 

105 """ 

106 

107 try: 

108 if pretty: 

109 return json.dumps( 

110 x, 

111 check_circular=False, 

112 default=default or _json_default, 

113 ensure_ascii=ensure_ascii, 

114 indent=4, 

115 sort_keys=True, 

116 ) 

117 return json.dumps( 

118 x, 

119 check_circular=False, 

120 default=default or _json_default, 

121 ensure_ascii=ensure_ascii, 

122 ) 

123 except Exception as exc: 

124 raise Error() from exc 

125 

126 

127def to_pretty_string(x, ensure_ascii: bool = True, default=None) -> str: 

128 """Encode an object as a JSON string with sorted keys and indentation. 

129 

130 Args: 

131 x: Object to encode. 

132 ensure_ascii: If ``True``, escape non-ASCII characters. 

133 default: Function that returns a serializable version of an object that is not serializable otherwise. 

134 By default, objects are converted to their ``vars``, or to strings if they have none. 

135 

136 Returns: 

137 The JSON string. 

138 

139 Raises: 

140 ``Error``: If the object cannot be encoded. 

141 """ 

142 

143 return to_string(x, pretty=True, ensure_ascii=ensure_ascii, default=default) 

144 

145 

146def _json_default(x): 

147 """Convert an object to its attribute dict, or to a string if it has none.""" 

148 try: 

149 return vars(x) 

150 except TypeError: 

151 return str(x)