tretool 0.2.1__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tretool/jsonlib.py CHANGED
@@ -1,81 +1,146 @@
1
- import chardet
2
- from typing import Any, Union, Dict, List, BinaryIO
1
+ import re
2
+ from typing import Any, Union, Dict, List, Callable, BinaryIO, TextIO, Optional, Tuple
3
+ from datetime import datetime, date, time
4
+ from decimal import Decimal
5
+ from enum import Enum
6
+ from pathlib import Path
7
+ import base64
8
+ import uuid
3
9
 
4
- class JSONEncodeError(Exception):
10
+ class JSONError(Exception):
11
+ """JSON 处理基类异常"""
12
+ pass
13
+
14
+ class JSONEncodeError(JSONError):
5
15
  """JSON 编码异常"""
6
16
  pass
7
17
 
8
- class JSONDecodeError(Exception):
18
+ class JSONDecodeError(JSONError):
9
19
  """JSON 解码异常"""
10
20
  pass
11
21
 
12
- def dump_to_str(obj: Any, indent: Union[int, None] = None) -> str:
22
+ class JSONEncoder:
13
23
  """
14
- Python 对象转换为 JSON 字符串
15
-
16
- 参数:
17
- obj: 要序列化的 Python 对象
18
- indent: 缩进空格数(None 表示紧凑格式)
24
+ 增强版 JSON 编码器
19
25
 
20
- 返回:
21
- JSON 格式字符串
22
-
23
- 异常:
24
- JSONEncodeError: 当遇到不可序列化的对象时
26
+ 特性:
27
+ - 支持常见 Python 类型的序列化
28
+ - 可扩展的自定义类型处理
29
+ - 格式化输出控制
30
+ - 循环引用检测
31
+ - 特殊字符转义
25
32
  """
26
- if indent is not None and not isinstance(indent, int):
27
- raise TypeError("indent must be int or None")
28
33
 
29
- return _Encoder(indent).encode(obj)
30
-
31
- def load_from_str(json_str: str) -> Any:
32
- """
33
- JSON 字符串解析为 Python 对象
34
-
35
- 参数:
36
- json_str: JSON 格式字符串
37
-
38
- 返回:
39
- 对应的 Python 对象
40
-
41
- 异常:
42
- JSONDecodeError: 当 JSON 格式无效时
43
- """
44
- parser = _Parser(json_str)
45
- return parser.parse()
46
-
47
- class _Encoder:
48
- """JSON 编码器实现"""
49
-
50
- def __init__(self, indent: Union[int, None] = None):
34
+ def __init__(
35
+ self,
36
+ *,
37
+ indent: Optional[int] = None,
38
+ ensure_ascii: bool = True,
39
+ sort_keys: bool = False,
40
+ skipkeys: bool = False,
41
+ allow_nan: bool = True,
42
+ default: Optional[Callable[[Any], Any]] = None,
43
+ use_decimal: bool = False,
44
+ datetime_format: str = '%Y-%m-%d %H:%M:%S',
45
+ date_format: str = '%Y-%m-%d',
46
+ time_format: str = '%H:%M:%S'
47
+ ):
48
+ """
49
+ 初始化编码器
50
+
51
+ 参数:
52
+ indent: 缩进空格数
53
+ ensure_ascii: 是否确保 ASCII 输出
54
+ sort_keys: 是否按键排序
55
+ skipkeys: 是否跳过非字符串键
56
+ allow_nan: 是否允许 NaN/Infinity
57
+ default: 默认转换函数
58
+ use_decimal: 是否使用 Decimal 处理浮点数
59
+ datetime_format: 日期时间格式
60
+ date_format: 日期格式
61
+ time_format: 时间格式
62
+ """
51
63
  self.indent = indent
64
+ self.ensure_ascii = ensure_ascii
65
+ self.sort_keys = sort_keys
66
+ self.skipkeys = skipkeys
67
+ self.allow_nan = allow_nan
68
+ self.default = default
69
+ self.use_decimal = use_decimal
70
+ self.datetime_format = datetime_format
71
+ self.date_format = date_format
72
+ self.time_format = time_format
52
73
  self._current_indent = 0
74
+ self._memo = set()
53
75
 
54
76
  def encode(self, obj: Any) -> str:
55
- if obj is None:
56
- return "null"
57
- elif isinstance(obj, bool):
58
- return "true" if obj else "false"
59
- elif isinstance(obj, (int, float)):
60
- return self._encode_number(obj)
61
- elif isinstance(obj, str):
62
- return self._encode_string(obj)
63
- elif isinstance(obj, (list, tuple)):
64
- return self._encode_array(obj)
65
- elif isinstance(obj, dict):
66
- return self._encode_object(obj)
67
- else:
68
- raise JSONEncodeError(f"Object of type {type(obj)} is not JSON serializable")
77
+ """编码 Python 对象为 JSON 字符串"""
78
+ self._memo.clear()
79
+ return self._encode(obj)
80
+
81
+ def _encode(self, obj: Any) -> str:
82
+ """内部编码方法"""
83
+ obj_id = id(obj)
84
+
85
+ # 检查循环引用
86
+ if obj_id in self._memo:
87
+ raise JSONEncodeError("Circular reference detected")
88
+ self._memo.add(obj_id)
89
+
90
+ try:
91
+ if obj is None:
92
+ return "null"
93
+ elif isinstance(obj, bool):
94
+ return "true" if obj else "false"
95
+ elif isinstance(obj, (int, float)):
96
+ return self._encode_number(obj)
97
+ elif isinstance(obj, str):
98
+ return self._encode_string(obj)
99
+ elif isinstance(obj, (list, tuple, set, frozenset)):
100
+ return self._encode_array(obj)
101
+ elif isinstance(obj, dict):
102
+ return self._encode_object(obj)
103
+ elif isinstance(obj, (datetime, date, time)):
104
+ return self._encode_datetime(obj)
105
+ elif isinstance(obj, Decimal):
106
+ return self._encode_decimal(obj)
107
+ elif isinstance(obj, (bytes, bytearray)):
108
+ return self._encode_bytes(obj)
109
+ elif isinstance(obj, Enum):
110
+ return self._encode(obj.value)
111
+ elif isinstance(obj, (Path, uuid.UUID)):
112
+ return self._encode_string(str(obj))
113
+ elif self.default is not None:
114
+ return self._encode(self.default(obj))
115
+ else:
116
+ raise JSONEncodeError(f"Object of type {type(obj)} is not JSON serializable")
117
+ finally:
118
+ self._memo.discard(obj_id)
69
119
 
70
120
  def _encode_number(self, num: Union[int, float]) -> str:
121
+ """编码数字"""
71
122
  if isinstance(num, int):
72
123
  return str(num)
73
- elif num.is_integer():
124
+
125
+ if not self.allow_nan:
126
+ if num != num: # NaN
127
+ raise JSONEncodeError("NaN is not allowed")
128
+ if num in (float('inf'), float('-inf')):
129
+ raise JSONEncodeError("Infinity is not allowed")
130
+
131
+ if self.use_decimal and isinstance(num, float):
132
+ return str(Decimal.from_float(num).normalize())
133
+
134
+ if num.is_integer():
74
135
  return str(int(num))
75
- else:
76
- return str(num)
136
+
137
+ return str(num)
77
138
 
78
139
  def _encode_string(self, s: str) -> str:
140
+ """编码字符串"""
141
+ if not self.ensure_ascii:
142
+ return f'"{s}"'
143
+
79
144
  escape_map = {
80
145
  '\"': '\\"',
81
146
  '\\': '\\\\',
@@ -92,53 +157,196 @@ class _Encoder:
92
157
  result.append(escape_map[char])
93
158
  elif ord(char) < 0x20:
94
159
  result.append(f"\\u{ord(char):04x}")
160
+ elif ord(char) > 0x7f:
161
+ result.append(f"\\u{ord(char):04x}")
95
162
  else:
96
163
  result.append(char)
97
164
 
98
165
  return f'"{"".join(result)}"'
99
166
 
100
- def _encode_array(self, array: List[Any]) -> str:
167
+ def _encode_array(self, array: Union[List[Any], Tuple[Any, ...], set, frozenset]) -> str:
168
+ """编码数组"""
101
169
  if not array:
102
170
  return "[]"
103
171
 
104
172
  if self.indent is None:
105
- items = [self.encode(item) for item in array]
173
+ items = [self._encode(item) for item in array]
106
174
  return f"[{','.join(items)}]"
107
175
  else:
108
176
  self._current_indent += self.indent
109
177
  indent_str = "\n" + " " * self._current_indent
110
- items = [f"{indent_str}{self.encode(item)}" for item in array]
178
+ items = [f"{indent_str}{self._encode(item)}" for item in array]
111
179
  self._current_indent -= self.indent
112
180
  return f"[{','.join(items)}\n{' ' * self._current_indent}]"
113
181
 
114
182
  def _encode_object(self, obj: Dict[str, Any]) -> str:
183
+ """编码对象"""
115
184
  if not obj:
116
185
  return "{}"
117
186
 
187
+ if self.sort_keys:
188
+ items = sorted(obj.items(), key=lambda x: x[0])
189
+ else:
190
+ items = obj.items()
191
+
118
192
  if self.indent is None:
119
- items = [f"{self.encode(k)}:{self.encode(v)}" for k, v in obj.items()]
120
- return f"{{{','.join(items)}}}"
193
+ pairs = []
194
+ for k, v in items:
195
+ if not isinstance(k, str):
196
+ if self.skipkeys:
197
+ continue
198
+ raise JSONEncodeError("Keys must be strings")
199
+ pairs.append(f"{self._encode_string(k)}:{self._encode(v)}")
200
+ return f"{{{','.join(pairs)}}}"
121
201
  else:
122
202
  self._current_indent += self.indent
123
203
  indent_str = "\n" + " " * self._current_indent
124
- items = []
125
- for k, v in obj.items():
126
- key_str = self.encode(k)
127
- value_str = self.encode(v)
128
- items.append(f"{indent_str}{key_str}: {value_str}")
204
+ pairs = []
205
+ for k, v in items:
206
+ if not isinstance(k, str):
207
+ if self.skipkeys:
208
+ continue
209
+ raise JSONEncodeError("Keys must be strings")
210
+ key_str = self._encode_string(k)
211
+ value_str = self._encode(v)
212
+ pairs.append(f"{indent_str}{key_str}: {value_str}")
129
213
  self._current_indent -= self.indent
130
- return f"{{{','.join(items)}\n{' ' * self._current_indent}}}"
214
+ return f"{{{','.join(pairs)}\n{' ' * self._current_indent}}}"
215
+
216
+ def _encode_datetime(self, dt: Union[datetime, date, time]) -> str:
217
+ """编码日期时间"""
218
+ if isinstance(dt, datetime):
219
+ return self._encode_string(dt.strftime(self.datetime_format))
220
+ elif isinstance(dt, date):
221
+ return self._encode_string(dt.strftime(self.date_format))
222
+ elif isinstance(dt, time):
223
+ return self._encode_string(dt.strftime(self.time_format))
224
+
225
+ def _encode_decimal(self, dec: Decimal) -> str:
226
+ """编码 Decimal"""
227
+ if self.use_decimal:
228
+ return str(dec.normalize())
229
+ return str(float(dec))
230
+
231
+ def _encode_bytes(self, b: Union[bytes, bytearray]) -> str:
232
+ """编码字节数据"""
233
+ return self._encode_string(base64.b64encode(b).decode('ascii'))
234
+
235
+ class JSONDecoder:
236
+ """
237
+ 增强版 JSON 解码器
238
+
239
+ 特性:
240
+ - 严格的 JSON 语法验证
241
+ - 支持自定义对象钩子
242
+ - 日期时间自动解析
243
+ - 错误位置报告
244
+ """
245
+
246
+ def __init__(
247
+ self,
248
+ *,
249
+ object_hook: Optional[Callable[[Dict[str, Any]], Any]] = None,
250
+ parse_float: Optional[Callable[[str], Any]] = None,
251
+ parse_int: Optional[Callable[[str], Any]] = None,
252
+ parse_constant: Optional[Callable[[str], Any]] = None,
253
+ datetime_format: str = '%Y-%m-%d %H:%M:%S',
254
+ date_format: str = '%Y-%m-%d',
255
+ time_format: str = '%H:%M:%S',
256
+ strict: bool = True
257
+ ):
258
+ """
259
+ 初始化解码器
260
+
261
+ 参数:
262
+ object_hook: 对象解析钩子
263
+ parse_float: 浮点数解析函数
264
+ parse_int: 整数解析函数
265
+ parse_constant: 常量解析函数
266
+ datetime_format: 日期时间格式
267
+ date_format: 日期格式
268
+ time_format: 时间格式
269
+ strict: 是否严格模式
270
+ """
271
+ self.object_hook = object_hook
272
+ self.parse_float = parse_float or float
273
+ self.parse_int = parse_int or int
274
+ self.parse_constant = parse_constant or (lambda x: None)
275
+ self.datetime_format = datetime_format
276
+ self.date_format = date_format
277
+ self.time_format = time_format
278
+ self.strict = strict
279
+
280
+ def decode(self, json_str: str) -> Any:
281
+ """解码 JSON 字符串为 Python 对象"""
282
+ if not isinstance(json_str, str):
283
+ raise JSONDecodeError("Input must be a string")
284
+
285
+ parser = _JSONParser(
286
+ json_str,
287
+ object_hook=self.object_hook,
288
+ parse_float=self.parse_float,
289
+ parse_int=self.parse_int,
290
+ parse_constant=self.parse_constant,
291
+ datetime_format=self.datetime_format,
292
+ date_format=self.date_format,
293
+ time_format=self.time_format,
294
+ strict=self.strict
295
+ )
296
+ return parser.parse()
131
297
 
132
- class _Parser:
298
+ class _JSONParser:
133
299
  """JSON 解析器实现"""
134
300
 
135
- def __init__(self, json_str: str):
136
- self.json_str = json_str.strip()
301
+ WHITESPACE = re.compile(r'[\s\n\r\t]*')
302
+ NUMBER_RE = re.compile(
303
+ r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?',
304
+ re.ASCII
305
+ )
306
+
307
+ def __init__(
308
+ self,
309
+ json_str: str,
310
+ *,
311
+ object_hook: Optional[Callable[[Dict[str, Any]], Any]] = None,
312
+ parse_float: Optional[Callable[[str], Any]] = None,
313
+ parse_int: Optional[Callable[[str], Any]] = None,
314
+ parse_constant: Optional[Callable[[str], Any]] = None,
315
+ datetime_format: str = '%Y-%m-%d %H:%M:%S',
316
+ date_format: str = '%Y-%m-%d',
317
+ time_format: str = '%H:%M:%S',
318
+ strict: bool = True
319
+ ):
320
+ self.json_str = json_str
137
321
  self.idx = 0
138
322
  self.len = len(json_str)
323
+ self.object_hook = object_hook
324
+ self.parse_float = parse_float
325
+ self.parse_int = parse_int
326
+ self.parse_constant = parse_constant
327
+ self.datetime_format = datetime_format
328
+ self.date_format = date_format
329
+ self.time_format = time_format
330
+ self.strict = strict
139
331
 
140
332
  def parse(self) -> Any:
141
- char = self._peek()
333
+ """解析 JSON 字符串"""
334
+ self._skip_whitespace()
335
+ value = self._parse_value()
336
+ self._skip_whitespace()
337
+
338
+ if self.idx != self.len:
339
+ raise JSONDecodeError(
340
+ f"Extra data at position {self.idx}",
341
+ self.json_str,
342
+ self.idx
343
+ )
344
+
345
+ return value
346
+
347
+ def _parse_value(self) -> Any:
348
+ """解析 JSON 值"""
349
+ char = self.json_str[self.idx]
142
350
 
143
351
  if char == '{':
144
352
  return self._parse_object()
@@ -146,75 +354,104 @@ class _Parser:
146
354
  return self._parse_array()
147
355
  elif char == '"':
148
356
  return self._parse_string()
149
- elif char == 'n' and self._peek_next(4) == 'null':
150
- self.idx += 4
357
+ elif char == 'n' and self._match('null'):
151
358
  return None
152
- elif char == 't' and self._peek_next(4) == 'true':
153
- self.idx += 4
359
+ elif char == 't' and self._match('true'):
154
360
  return True
155
- elif char == 'f' and self._peek_next(5) == 'false':
156
- self.idx += 5
361
+ elif char == 'f' and self._match('false'):
157
362
  return False
158
363
  elif char == '-' or char.isdigit():
159
364
  return self._parse_number()
365
+ elif char == 'N' and self._match('NaN'):
366
+ return self.parse_constant('NaN')
367
+ elif char == 'I' and self._match('Infinity'):
368
+ return self.parse_constant('Infinity')
369
+ elif char == '-' and self._match('-Infinity'):
370
+ return self.parse_constant('-Infinity')
160
371
  else:
161
- raise JSONDecodeError(f"Unexpected character at position {self.idx}: {char}")
372
+ raise JSONDecodeError(
373
+ f"Unexpected character at position {self.idx}: {char}",
374
+ self.json_str,
375
+ self.idx
376
+ )
162
377
 
163
378
  def _parse_object(self) -> Dict[str, Any]:
379
+ """解析 JSON 对象"""
164
380
  obj = {}
165
381
  self._consume('{')
382
+ self._skip_whitespace()
166
383
 
167
- while self._peek() != '}':
384
+ if self.json_str[self.idx] == '}':
385
+ self._consume('}')
386
+ return obj
387
+
388
+ while True:
168
389
  # 解析键
169
390
  key = self._parse_string()
391
+ self._skip_whitespace()
170
392
  self._consume(':')
393
+ self._skip_whitespace()
171
394
 
172
395
  # 解析值
173
- value = self.parse()
396
+ value = self._parse_value()
174
397
  obj[key] = value
175
398
 
176
- # 处理逗号或结束
177
- if self._peek() == ',':
178
- self._consume(',')
179
- elif self._peek() != '}':
180
- raise JSONDecodeError("Expected ',' or '}' after object pair")
399
+ self._skip_whitespace()
400
+ if self.json_str[self.idx] == '}':
401
+ self._consume('}')
402
+ break
403
+ self._consume(',')
404
+ self._skip_whitespace()
181
405
 
182
- self._consume('}')
406
+ if self.object_hook is not None:
407
+ return self.object_hook(obj)
183
408
  return obj
184
409
 
185
410
  def _parse_array(self) -> List[Any]:
411
+ """解析 JSON 数组"""
186
412
  arr = []
187
413
  self._consume('[')
414
+ self._skip_whitespace()
415
+
416
+ if self.json_str[self.idx] == ']':
417
+ self._consume(']')
418
+ return arr
188
419
 
189
- while self._peek() != ']':
420
+ while True:
190
421
  # 解析元素
191
- arr.append(self.parse())
422
+ arr.append(self._parse_value())
423
+ self._skip_whitespace()
192
424
 
193
- # 处理逗号或结束
194
- if self._peek() == ',':
195
- self._consume(',')
196
- elif self._peek() != ']':
197
- raise JSONDecodeError("Expected ',' or ']' after array element")
425
+ if self.json_str[self.idx] == ']':
426
+ self._consume(']')
427
+ break
428
+ self._consume(',')
429
+ self._skip_whitespace()
198
430
 
199
- self._consume(']')
200
431
  return arr
201
432
 
202
433
  def _parse_string(self) -> str:
434
+ """解析 JSON 字符串"""
203
435
  self._consume('"')
204
436
  chars = []
205
437
 
206
- while self._peek() != '"':
207
- char = self._peek()
438
+ while self.json_str[self.idx] != '"':
439
+ char = self.json_str[self.idx]
208
440
 
209
441
  if char == '\\':
210
442
  self._consume('\\')
211
- esc_char = self._peek()
443
+ esc_char = self.json_str[self.idx]
444
+
212
445
  if esc_char == 'u':
213
446
  # Unicode 转义
214
447
  self._consume('u')
215
448
  hex_str = self.json_str[self.idx:self.idx+4]
216
449
  if len(hex_str) != 4:
217
- raise JSONDecodeError("Invalid Unicode escape sequence")
450
+ raise JSONDecodeError(
451
+ "Invalid Unicode escape sequence",
452
+ self.json_str,
453
+ self.idx
454
+ )
218
455
  self.idx += 4
219
456
  chars.append(chr(int(hex_str, 16)))
220
457
  else:
@@ -232,68 +469,299 @@ class _Parser:
232
469
  chars.append(escape_map.get(esc_char, esc_char))
233
470
  self._consume(esc_char)
234
471
  else:
472
+ if ord(char) < 0x20 and self.strict:
473
+ raise JSONDecodeError(
474
+ "Invalid control character in string",
475
+ self.json_str,
476
+ self.idx
477
+ )
235
478
  chars.append(char)
236
479
  self._consume(char)
237
480
 
238
481
  self._consume('"')
239
- return ''.join(chars)
240
-
241
- def _parse_number(self) -> Union[int, float]:
242
- start_idx = self.idx
243
- is_float = False
244
-
245
- # 处理符号
246
- if self._peek() == '-':
247
- self._consume('-')
248
-
249
- # 整数部分
250
- while self._peek().isdigit():
251
- self._consume()
252
-
253
- # 小数部分
254
- if self._peek() == '.':
255
- is_float = True
256
- self._consume('.')
257
- while self._peek().isdigit():
258
- self._consume()
259
-
260
- # 指数部分
261
- if self._peek().lower() == 'e':
262
- is_float = True
263
- self._consume()
264
- if self._peek() in ('+', '-'):
265
- self._consume()
266
- while self._peek().isdigit():
267
- self._consume()
268
-
269
- num_str = self.json_str[start_idx:self.idx]
482
+ s = ''.join(chars)
483
+
484
+ # 尝试解析日期时间
270
485
  try:
271
- return float(num_str) if is_float else int(num_str)
486
+ if len(s) == len(self.datetime_format):
487
+ return datetime.strptime(s, self.datetime_format)
488
+ elif len(s) == len(self.date_format):
489
+ return datetime.strptime(s, self.date_format).date()
490
+ elif len(s) == len(self.time_format):
491
+ return datetime.strptime(s, self.time_format).time()
272
492
  except ValueError:
273
- raise JSONDecodeError(f"Invalid number literal: {num_str}")
493
+ pass
494
+
495
+ return s
274
496
 
275
- def _peek(self) -> str:
276
- if self.idx >= self.len:
277
- raise JSONDecodeError("Unexpected end of JSON input")
278
- return self.json_str[self.idx]
497
+ def _parse_number(self) -> Union[int, float]:
498
+ """解析 JSON 数字"""
499
+ match = self.NUMBER_RE.match(self.json_str, self.idx)
500
+ if not match:
501
+ raise JSONDecodeError(
502
+ "Invalid number literal",
503
+ self.json_str,
504
+ self.idx
505
+ )
506
+
507
+ integer, fraction, exponent = match.groups()
508
+ self.idx = match.end()
509
+
510
+ if fraction or exponent:
511
+ num_str = integer + (fraction or '') + (exponent or '')
512
+ return self.parse_float(num_str)
513
+ else:
514
+ return self.parse_int(integer)
279
515
 
280
- def _peek_next(self, n: int) -> str:
281
- if self.idx + n > self.len:
282
- raise JSONDecodeError("Unexpected end of JSON input")
283
- return self.json_str[self.idx:self.idx+n]
516
+ def _skip_whitespace(self) -> None:
517
+ """跳过空白字符"""
518
+ while self.idx < self.len and self.json_str[self.idx] in ' \t\n\r':
519
+ self.idx += 1
284
520
 
285
- def _consume(self, expected: str = None):
286
- if expected is not None and self._peek() != expected:
287
- raise JSONDecodeError(f"Expected '{expected}' at position {self.idx}")
521
+ def _consume(self, expected: str) -> None:
522
+ """消费指定字符"""
523
+ if self.idx >= self.len or self.json_str[self.idx] != expected:
524
+ raise JSONDecodeError(
525
+ f"Expected '{expected}' at position {self.idx}",
526
+ self.json_str,
527
+ self.idx
528
+ )
288
529
  self.idx += 1
530
+
531
+ def _match(self, literal: str) -> bool:
532
+ """匹配指定字符串"""
533
+ if self.json_str.startswith(literal, self.idx):
534
+ self.idx += len(literal)
535
+ return True
536
+ return False
537
+
538
+ # 高级接口函数
539
+ def dumps(
540
+ obj: Any,
541
+ *,
542
+ indent: Optional[int] = None,
543
+ ensure_ascii: bool = True,
544
+ sort_keys: bool = False,
545
+ skipkeys: bool = False,
546
+ allow_nan: bool = True,
547
+ default: Optional[Callable[[Any], Any]] = None,
548
+ use_decimal: bool = False,
549
+ **kwargs
550
+ ) -> str:
551
+ """
552
+ 将 Python 对象序列化为 JSON 字符串
553
+
554
+ 参数:
555
+ obj: 要序列化的对象
556
+ indent: 缩进空格数
557
+ ensure_ascii: 是否确保 ASCII 输出
558
+ sort_keys: 是否按键排序
559
+ skipkeys: 是否跳过非字符串键
560
+ allow_nan: 是否允许 NaN/Infinity
561
+ default: 默认转换函数
562
+ use_decimal: 是否使用 Decimal 处理浮点数
563
+ kwargs: 其他编码器参数
564
+
565
+ 返回:
566
+ JSON 字符串
567
+ """
568
+ encoder = JSONEncoder(
569
+ indent=indent,
570
+ ensure_ascii=ensure_ascii,
571
+ sort_keys=sort_keys,
572
+ skipkeys=skipkeys,
573
+ allow_nan=allow_nan,
574
+ default=default,
575
+ use_decimal=use_decimal,
576
+ **kwargs
577
+ )
578
+ return encoder.encode(obj)
579
+
580
+ def loads(
581
+ json_str: str,
582
+ *,
583
+ object_hook: Optional[Callable[[Dict[str, Any]], Any]] = None,
584
+ parse_float: Optional[Callable[[str], Any]] = None,
585
+ parse_int: Optional[Callable[[str], Any]] = None,
586
+ parse_constant: Optional[Callable[[str], Any]] = None,
587
+ strict: bool = True,
588
+ **kwargs
589
+ ) -> Any:
590
+ """
591
+ 将 JSON 字符串解析为 Python 对象
592
+
593
+ 参数:
594
+ json_str: JSON 字符串
595
+ object_hook: 对象解析钩子
596
+ parse_float: 浮点数解析函数
597
+ parse_int: 整数解析函数
598
+ parse_constant: 常量解析函数
599
+ strict: 是否严格模式
600
+ kwargs: 其他解码器参数
601
+
602
+ 返回:
603
+ Python 对象
604
+ """
605
+ decoder = JSONDecoder(
606
+ object_hook=object_hook,
607
+ parse_float=parse_float,
608
+ parse_int=parse_int,
609
+ parse_constant=parse_constant,
610
+ strict=strict,
611
+ **kwargs
612
+ )
613
+ return decoder.decode(json_str)
289
614
 
290
- # 简化版接口
291
- def dump_to_file(obj: Any, filepath: str, indent: Union[int, None] = 4, encoding:str='utf-8'):
292
- """将 Python 对象序列化为 JSON 格式并写入文件"""
293
- with open(filepath, 'w', encoding=encoding) as file:
294
- file.write(dump_to_str(obj, indent))
615
+ def dump(
616
+ obj: Any,
617
+ file: TextIO,
618
+ *,
619
+ indent: Optional[int] = None,
620
+ ensure_ascii: bool = True,
621
+ sort_keys: bool = False,
622
+ skipkeys: bool = False,
623
+ allow_nan: bool = True,
624
+ default: Optional[Callable[[Any], Any]] = None,
625
+ use_decimal: bool = False,
626
+ **kwargs
627
+ ) -> None:
628
+ """
629
+ 将 Python 对象序列化为 JSON 并写入文件
630
+
631
+ 参数:
632
+ obj: 要序列化的对象
633
+ file: 文件对象
634
+ indent: 缩进空格数
635
+ ensure_ascii: 是否确保 ASCII 输出
636
+ sort_keys: 是否按键排序
637
+ skipkeys: 是否跳过非字符串键
638
+ allow_nan: 是否允许 NaN/Infinity
639
+ default: 默认转换函数
640
+ use_decimal: 是否使用 Decimal 处理浮点数
641
+ kwargs: 其他编码器参数
642
+ """
643
+ json_str = dumps(
644
+ obj,
645
+ indent=indent,
646
+ ensure_ascii=ensure_ascii,
647
+ sort_keys=sort_keys,
648
+ skipkeys=skipkeys,
649
+ allow_nan=allow_nan,
650
+ default=default,
651
+ use_decimal=use_decimal,
652
+ **kwargs
653
+ )
654
+ file.write(json_str)
295
655
 
296
- def load_from_file(filepath:str, encoding:str='utf-8'):
297
- """从文件读取 JSON 数据并解析为 Python 对象"""
298
- with open(filepath, 'r', encoding=encoding) as file:
299
- return load_from_str(file.read())
656
+ def load(
657
+ file: TextIO,
658
+ *,
659
+ object_hook: Optional[Callable[[Dict[str, Any]], Any]] = None,
660
+ parse_float: Optional[Callable[[str], Any]] = None,
661
+ parse_int: Optional[Callable[[str], Any]] = None,
662
+ parse_constant: Optional[Callable[[str], Any]] = None,
663
+ strict: bool = True,
664
+ **kwargs
665
+ ) -> Any:
666
+ """
667
+ 从文件读取 JSON 数据并解析为 Python 对象
668
+
669
+ 参数:
670
+ fp: 文件对象
671
+ object_hook: 对象解析钩子
672
+ parse_float: 浮点数解析函数
673
+ parse_int: 整数解析函数
674
+ parse_constant: 常量解析函数
675
+ strict: 是否严格模式
676
+ kwargs: 其他解码器参数
677
+
678
+ 返回:
679
+ Python 对象
680
+ """
681
+ return loads(file.read(), object_hook=object_hook, parse_float=parse_float,
682
+ parse_int=parse_int, parse_constant=parse_constant,
683
+ strict=strict, **kwargs)
684
+
685
+ # 便捷函数
686
+ # 便捷函数
687
+ def dump_to_file(
688
+ obj: Any,
689
+ filepath: Union[str, Path],
690
+ encoding: str = 'utf-8',
691
+ *,
692
+ indent: Optional[int] = None,
693
+ ensure_ascii: bool = True,
694
+ sort_keys: bool = False,
695
+ skipkeys: bool = False,
696
+ allow_nan: bool = True,
697
+ default: Optional[Callable[[Any], Any]] = None,
698
+ use_decimal: bool = False,
699
+ **kwargs
700
+ ) -> None:
701
+ """
702
+ 将 Python 对象序列化为 JSON 并写入文件
703
+
704
+ 参数:
705
+ obj: 要序列化的对象
706
+ filepath: 文件路径
707
+ encoding: 文件编码
708
+ indent: 缩进空格数
709
+ ensure_ascii: 是否确保 ASCII 输出
710
+ sort_keys: 是否按键排序
711
+ skipkeys: 是否跳过非字符串键
712
+ allow_nan: 是否允许 NaN/Infinity
713
+ default: 默认转换函数
714
+ use_decimal: 是否使用 Decimal 处理浮点数
715
+ kwargs: 其他编码器参数
716
+ """
717
+ with open(filepath, 'w', encoding=encoding) as f:
718
+ dump(
719
+ obj,
720
+ f, # 添加文件对象参数
721
+ indent=indent,
722
+ ensure_ascii=ensure_ascii,
723
+ sort_keys=sort_keys,
724
+ skipkeys=skipkeys,
725
+ allow_nan=allow_nan,
726
+ default=default,
727
+ use_decimal=use_decimal,
728
+ **kwargs
729
+ )
730
+
731
+ def load_from_file(
732
+ filepath: Union[str, Path],
733
+ encoding: str = 'utf-8',
734
+ *,
735
+ object_hook: Optional[Callable[[Dict[str, Any]], Any]] = None,
736
+ parse_float: Optional[Callable[[str], Any]] = None,
737
+ parse_int: Optional[Callable[[str], Any]] = None,
738
+ parse_constant: Optional[Callable[[str], Any]] = None,
739
+ strict: bool = True,
740
+ **kwargs
741
+ ) -> Any:
742
+ """
743
+ 从文件读取 JSON 数据并解析为 Python 对象
744
+
745
+ 参数:
746
+ filepath: 文件路径
747
+ encoding: 文件编码
748
+ object_hook: 对象解析钩子
749
+ parse_float: 浮点数解析函数
750
+ parse_int: 整数解析函数
751
+ parse_constant: 常量解析函数
752
+ strict: 是否严格模式
753
+ kwargs: 其他解码器参数
754
+
755
+ 返回:
756
+ Python 对象
757
+ """
758
+ with open(filepath, 'r', encoding=encoding) as f:
759
+ return load(
760
+ f,
761
+ object_hook=object_hook,
762
+ parse_float=parse_float,
763
+ parse_int=parse_int,
764
+ parse_constant=parse_constant,
765
+ strict=strict,
766
+ **kwargs
767
+ )