Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/io/json/_json.py: 20%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

618 statements  

1from __future__ import annotations 

2 

3from abc import ( 

4 ABC, 

5 abstractmethod, 

6) 

7from collections import abc 

8from itertools import islice 

9from typing import ( 

10 TYPE_CHECKING, 

11 Any, 

12 Generic, 

13 Literal, 

14 Self, 

15 TypeVar, 

16 final, 

17 overload, 

18) 

19import warnings 

20 

21import numpy as np 

22 

23from pandas._config import option_context 

24 

25from pandas._libs import lib 

26from pandas._libs._ujson import ( 

27 ujson_dumps, 

28 ujson_loads, 

29) 

30from pandas._libs.tslibs import iNaT 

31from pandas.compat._optional import import_optional_dependency 

32from pandas.errors import ( 

33 AbstractMethodError, 

34 OutOfBoundsDatetime, 

35 Pandas4Warning, 

36) 

37from pandas.util._decorators import set_module 

38from pandas.util._exceptions import find_stack_level 

39from pandas.util._validators import check_dtype_backend 

40 

41from pandas.core.dtypes.common import ( 

42 ensure_str, 

43 is_string_dtype, 

44 pandas_dtype, 

45) 

46from pandas.core.dtypes.dtypes import ( 

47 PeriodDtype, 

48 SparseDtype, 

49) 

50 

51from pandas import ( 

52 ArrowDtype, 

53 DataFrame, 

54 Index, 

55 MultiIndex, 

56 Series, 

57 isna, 

58 notna, 

59 to_datetime, 

60) 

61from pandas.core.reshape.concat import concat 

62 

63from pandas.io._util import arrow_table_to_pandas 

64from pandas.io.common import ( 

65 IOHandles, 

66 dedup_names, 

67 get_handle, 

68 is_potential_multi_index, 

69 stringify_path, 

70) 

71from pandas.io.json._normalize import convert_to_line_delimits 

72from pandas.io.json._table_schema import ( 

73 build_table_schema, 

74 parse_table_schema, 

75 set_default_names, 

76) 

77from pandas.io.parsers.readers import validate_integer 

78 

79if TYPE_CHECKING: 

80 from collections.abc import ( 

81 Callable, 

82 Hashable, 

83 Mapping, 

84 ) 

85 from types import TracebackType 

86 

87 from pandas._typing import ( 

88 CompressionOptions, 

89 DtypeArg, 

90 DtypeBackend, 

91 DtypeObj, 

92 FilePath, 

93 IndexLabel, 

94 JSONEngine, 

95 JSONSerializable, 

96 ReadBuffer, 

97 StorageOptions, 

98 WriteBuffer, 

99 ) 

100 

101 from pandas.core.generic import NDFrame 

102 

103FrameSeriesStrT = TypeVar("FrameSeriesStrT", bound=Literal["frame", "series"]) 

104 

105 

106def _has_dt_accessor(dtype: DtypeObj) -> bool: 

107 """ 

108 Whether this dtype takes the ``.dt.as_unit`` conversion below. 

109 

110 SparseDtype reports the subtype's kind, so the kind check alone is not 

111 enough. This is not an exhaustive test for a usable ``.dt.as_unit`` -- it 

112 only rules out what is known to reach here. Datetime-likes it returns False 

113 for are scaled to date_unit by the C encoder instead. 

114 """ 

115 return dtype.kind in "Mm" and not isinstance(dtype, SparseDtype) 

116 

117 

118# interface to/from 

119@overload 

120def to_json( 

121 path_or_buf: FilePath | WriteBuffer[str] | WriteBuffer[bytes], 

122 obj: NDFrame, 

123 orient: str | None = ..., 

124 date_format: str = ..., 

125 double_precision: int = ..., 

126 force_ascii: bool = ..., 

127 date_unit: str = ..., 

128 default_handler: Callable[[Any], JSONSerializable] | None = ..., 

129 lines: bool = ..., 

130 compression: CompressionOptions = ..., 

131 index: bool | None = ..., 

132 indent: int = ..., 

133 storage_options: StorageOptions = ..., 

134 mode: Literal["a", "w"] = ..., 

135) -> None: ... 

136 

137 

138@overload 

139def to_json( 

140 path_or_buf: None, 

141 obj: NDFrame, 

142 orient: str | None = ..., 

143 date_format: str = ..., 

144 double_precision: int = ..., 

145 force_ascii: bool = ..., 

146 date_unit: str = ..., 

147 default_handler: Callable[[Any], JSONSerializable] | None = ..., 

148 lines: bool = ..., 

149 compression: CompressionOptions = ..., 

150 index: bool | None = ..., 

151 indent: int = ..., 

152 storage_options: StorageOptions = ..., 

153 mode: Literal["a", "w"] = ..., 

154) -> str: ... 

155 

156 

157def to_json( 

158 path_or_buf: FilePath | WriteBuffer[str] | WriteBuffer[bytes] | None, 

159 obj: NDFrame, 

160 orient: str | None = None, 

161 date_format: str = "epoch", 

162 double_precision: int = 10, 

163 force_ascii: bool = True, 

164 date_unit: str = "ms", 

165 default_handler: Callable[[Any], JSONSerializable] | None = None, 

166 lines: bool = False, 

167 compression: CompressionOptions = "infer", 

168 index: bool | None = None, 

169 indent: int = 0, 

170 storage_options: StorageOptions | None = None, 

171 mode: Literal["a", "w"] = "w", 

172) -> str | None: 

173 if orient in ["records", "values"] and index is True: 

174 raise ValueError( 

175 "'index=True' is only valid when 'orient' is 'split', 'table', " 

176 "'index', or 'columns'." 

177 ) 

178 elif orient in ["index", "columns"] and index is False: 

179 raise ValueError( 

180 "'index=False' is only valid when 'orient' is 'split', 'table', " 

181 "'records', or 'values'." 

182 ) 

183 elif index is None: 

184 # will be ignored for orient='records' and 'values' 

185 index = True 

186 

187 if lines and orient != "records": 

188 raise ValueError("'lines' keyword only valid when 'orient' is records") 

189 

190 if mode not in ["a", "w"]: 

191 msg = ( 

192 f"mode={mode} is not a valid option." 

193 "Only 'w' and 'a' are currently supported." 

194 ) 

195 raise ValueError(msg) 

196 

197 if mode == "a" and (not lines or orient != "records"): 

198 msg = ( 

199 "mode='a' (append) is only supported when " 

200 "lines is True and orient is 'records'" 

201 ) 

202 raise ValueError(msg) 

203 

204 if orient == "table" and isinstance(obj, Series): 

205 obj = obj.to_frame(name=obj.name or "values") 

206 

207 if date_format == "epoch": 

208 # for epoch (numeric) format, convert datetime-likes to the desired 

209 # unit up front, such that the C ObjToJSON code can simply write out 

210 # the integer values without worrying about conversion 

211 if date_unit not in ["s", "ms", "us", "ns"]: 

212 raise ValueError(f"Invalid value '{date_unit}' for option 'date_unit'") 

213 if isinstance(obj, DataFrame): 

214 copied = False 

215 cols = np.nonzero(obj.dtypes.map(_has_dt_accessor))[0] 

216 if len(cols): 

217 obj = obj.copy(deep=False) 

218 copied = True 

219 for col in cols: 

220 obj.isetitem(col, obj.iloc[:, col].dt.as_unit(date_unit)) 

221 if _has_dt_accessor(obj.index.dtype): 

222 if not copied: 

223 obj = obj.copy(deep=False) 

224 copied = True 

225 obj.index = Series(obj.index).dt.as_unit(date_unit) 

226 if _has_dt_accessor(obj.columns.dtype): 

227 if not copied: 

228 obj = obj.copy(deep=False) 

229 copied = True 

230 obj.columns = Series(obj.columns).dt.as_unit(date_unit) 

231 elif isinstance(obj, Series): 

232 if _has_dt_accessor(obj.dtype): 

233 obj = obj.copy(deep=False) 

234 obj = obj.dt.as_unit(date_unit) 

235 if _has_dt_accessor(obj.index.dtype): 

236 obj = obj.copy(deep=False) 

237 obj.index = Series(obj.index).dt.as_unit(date_unit) 

238 

239 writer: type[Writer] 

240 if orient == "table" and isinstance(obj, DataFrame): 

241 writer = JSONTableWriter 

242 elif isinstance(obj, Series): 

243 writer = SeriesWriter 

244 elif isinstance(obj, DataFrame): 

245 writer = FrameWriter 

246 else: 

247 raise NotImplementedError("'obj' should be a Series or a DataFrame") 

248 

249 s = writer( 

250 obj, 

251 orient=orient, 

252 date_format=date_format, 

253 double_precision=double_precision, 

254 ensure_ascii=force_ascii, 

255 date_unit=date_unit, 

256 default_handler=default_handler, 

257 index=index, 

258 indent=indent, 

259 ).write() 

260 

261 if lines: 

262 s = convert_to_line_delimits(s) 

263 

264 if path_or_buf is not None: 

265 # apply compression and byte/text conversion 

266 with get_handle( 

267 path_or_buf, mode, compression=compression, storage_options=storage_options 

268 ) as handles: 

269 handles.handle.write(s) 

270 else: 

271 return s 

272 return None 

273 

274 

275class Writer(ABC): 

276 _default_orient: str 

277 

278 def __init__( 

279 self, 

280 obj: NDFrame, 

281 orient: str | None, 

282 date_format: str, 

283 double_precision: int, 

284 ensure_ascii: bool, 

285 date_unit: str, 

286 index: bool, 

287 default_handler: Callable[[Any], JSONSerializable] | None = None, 

288 indent: int = 0, 

289 ) -> None: 

290 self.obj = obj 

291 

292 if orient is None: 

293 orient = self._default_orient 

294 

295 self.orient = orient 

296 self.date_format = date_format 

297 self.double_precision = double_precision 

298 self.ensure_ascii = ensure_ascii 

299 self.date_unit = date_unit 

300 self.default_handler = default_handler 

301 self.index = index 

302 self.indent = indent 

303 self._format_axes() 

304 

305 def _format_axes(self) -> None: 

306 raise AbstractMethodError(self) 

307 

308 def write(self) -> str: 

309 iso_dates = self.date_format == "iso" 

310 try: 

311 return ujson_dumps( 

312 self.obj_to_write, 

313 orient=self.orient, 

314 double_precision=self.double_precision, 

315 ensure_ascii=self.ensure_ascii, 

316 date_unit=self.date_unit, 

317 iso_dates=iso_dates, 

318 default_handler=self.default_handler, 

319 indent=self.indent, 

320 ) 

321 except OverflowError as err: 

322 # GH#36211 the C encoder recurses without bound on object types it 

323 # does not understand. 

324 if "Maximum recursion level reached" not in str(err): 

325 raise 

326 raise ValueError( 

327 "Unable to serialize object to JSON: encountered an " 

328 "unsupported object type; convert the column to a supported " 

329 "type (e.g. str) before calling to_json." 

330 ) from err 

331 

332 @property 

333 @abstractmethod 

334 def obj_to_write(self) -> NDFrame | Mapping[IndexLabel, Any]: 

335 """Object to write in JSON format.""" 

336 

337 

338class SeriesWriter(Writer): 

339 _default_orient = "index" 

340 

341 @property 

342 def obj_to_write(self) -> NDFrame | Mapping[IndexLabel, Any]: 

343 if not self.index and self.orient == "split": 

344 return {"name": self.obj.name, "data": self.obj.values} 

345 else: 

346 return self.obj 

347 

348 def _format_axes(self) -> None: 

349 if not self.obj.index.is_unique and self.orient == "index": 

350 raise ValueError(f"Series index must be unique for orient='{self.orient}'") 

351 

352 

353class FrameWriter(Writer): 

354 _default_orient = "columns" 

355 

356 @property 

357 def obj_to_write(self) -> NDFrame | Mapping[IndexLabel, Any]: 

358 if not self.index and self.orient == "split": 

359 obj_to_write = self.obj.to_dict(orient="split") 

360 del obj_to_write["index"] 

361 else: 

362 obj_to_write = self.obj 

363 return obj_to_write 

364 

365 def _format_axes(self) -> None: 

366 """ 

367 Try to format axes if they are datelike. 

368 """ 

369 if not self.obj.index.is_unique and self.orient in ("index", "columns"): 

370 raise ValueError( 

371 f"DataFrame index must be unique for orient='{self.orient}'." 

372 ) 

373 if not self.obj.columns.is_unique and self.orient in ( 

374 "index", 

375 "columns", 

376 "records", 

377 ): 

378 raise ValueError( 

379 f"DataFrame columns must be unique for orient='{self.orient}'." 

380 ) 

381 

382 

383class JSONTableWriter(FrameWriter): 

384 _default_orient = "records" 

385 

386 def __init__( 

387 self, 

388 obj, 

389 orient: str | None, 

390 date_format: str, 

391 double_precision: int, 

392 ensure_ascii: bool, 

393 date_unit: str, 

394 index: bool, 

395 default_handler: Callable[[Any], JSONSerializable] | None = None, 

396 indent: int = 0, 

397 ) -> None: 

398 """ 

399 Adds a `schema` attribute with the Table Schema, resets 

400 the index (can't do in caller, because the schema inference needs 

401 to know what the index is, forces orient to records, and forces 

402 date_format to 'iso'. 

403 """ 

404 super().__init__( 

405 obj, 

406 orient, 

407 date_format, 

408 double_precision, 

409 ensure_ascii, 

410 date_unit, 

411 index, 

412 default_handler=default_handler, 

413 indent=indent, 

414 ) 

415 

416 if date_format != "iso": 

417 msg = ( 

418 "Trying to write with `orient='table'` and " 

419 f"`date_format='{date_format}'`. Table Schema requires dates " 

420 "to be formatted with `date_format='iso'`" 

421 ) 

422 raise ValueError(msg) 

423 

424 self.schema = build_table_schema(obj, index=self.index) 

425 if self.index: 

426 obj = set_default_names(obj) 

427 

428 # NotImplemented on a column MultiIndex 

429 if obj.ndim == 2 and isinstance(obj.columns, MultiIndex): 

430 raise NotImplementedError( 

431 "orient='table' is not supported for MultiIndex columns" 

432 ) 

433 

434 # TODO: Do this timedelta properly in objToJSON.c See GH #15137 

435 if ((obj.ndim == 1) and (obj.name in set(obj.index.names))) or len( 

436 obj.columns.intersection(obj.index.names) 

437 ): 

438 msg = "Overlapping names between the index and columns" 

439 raise ValueError(msg) 

440 

441 timedeltas = obj.select_dtypes(include=["timedelta"]).columns 

442 copied = False 

443 if len(timedeltas): 

444 obj = obj.copy() 

445 copied = True 

446 obj[timedeltas] = obj[timedeltas].map(lambda x: x.isoformat()) 

447 

448 # exclude index from obj if index=False 

449 if not self.index: 

450 self.obj = obj.reset_index(drop=True) 

451 else: 

452 # Convert PeriodIndex to datetimes before serializing 

453 if isinstance(obj.index.dtype, PeriodDtype): 

454 if not copied: 

455 obj = obj.copy(deep=False) 

456 obj.index = obj.index.to_timestamp() 

457 self.obj = obj.reset_index(drop=False) 

458 self.date_format = "iso" 

459 self.orient = "records" 

460 self.index = index 

461 

462 @property 

463 def obj_to_write(self) -> NDFrame | Mapping[IndexLabel, Any]: 

464 return {"schema": self.schema, "data": self.obj} 

465 

466 

467def _validate_pyarrow_engine_options( 

468 *, 

469 typ, 

470 dtype, 

471 convert_axes, 

472 convert_dates, 

473 keep_default_dates, 

474 precise_float, 

475 date_unit, 

476 encoding, 

477 encoding_errors, 

478 compression, 

479 storage_options, 

480) -> None: 

481 """ 

482 Reject ``read_json`` arguments the ``"pyarrow"`` engine cannot honor. 

483 

484 The pyarrow engine hands the input straight to ``pyarrow.json.read_json``, 

485 so any argument that pandas would otherwise apply itself is silently 

486 ignored, producing a wrong result rather than an error. Raise instead. 

487 

488 Called from ``read_json`` before ``convert_axes`` and ``dtype`` are 

489 resolved from ``None`` to ``True`` (and ``convert_dates`` / 

490 ``keep_default_dates`` from ``lib.no_default``), so that an 

491 explicitly-passed value is distinguishable from the default. 

492 """ 

493 

494 def raise_unsupported(argname: str) -> None: 

495 raise ValueError( 

496 f"The {argname!r} option is not supported with the 'pyarrow' engine" 

497 ) 

498 

499 if typ != "frame": 

500 raise_unsupported("typ") 

501 if convert_axes is not None: 

502 raise_unsupported("convert_axes") 

503 if convert_dates is not True and convert_dates is not lib.no_default: 

504 raise_unsupported("convert_dates") 

505 if keep_default_dates is not True and keep_default_dates is not lib.no_default: 

506 raise_unsupported("keep_default_dates") 

507 if precise_float is not False: 

508 raise_unsupported("precise_float") 

509 if date_unit is not None: 

510 raise_unsupported("date_unit") 

511 if storage_options is not None: 

512 raise_unsupported("storage_options") 

513 if compression != "infer": 

514 # pyarrow infers compression from the file extension itself; pandas 

515 # cannot honor an explicit method (or dict) or disable inference. 

516 raise_unsupported("compression") 

517 if encoding is not None and encoding.lower() not in ("utf-8", "utf8"): 

518 # pyarrow assumes UTF-8 and ignores the encoding argument entirely. 

519 raise_unsupported("encoding") 

520 if encoding_errors is not None and encoding_errors != "strict": 

521 raise_unsupported("encoding_errors") 

522 

523 if isinstance(dtype, dict): 

524 # GH#59516 supports a mapping of ArrowDtype; any other dtype in the 

525 # mapping is silently dropped, so reject it. 

526 for field, field_dtype in dtype.items(): 

527 if not isinstance(pandas_dtype(field_dtype), ArrowDtype): 

528 raise ValueError( 

529 "The 'pyarrow' engine only supports ArrowDtype values in " 

530 f"the 'dtype' mapping, got {field_dtype!r} for column " 

531 f"{field!r}" 

532 ) 

533 elif dtype is not None: 

534 # a scalar dtype (including True/False) applies to every column but is 

535 # silently ignored by the pyarrow engine. 

536 raise_unsupported("dtype") 

537 

538 

539@overload 

540def read_json( 

541 path_or_buf: FilePath | ReadBuffer[str] | ReadBuffer[bytes], 

542 *, 

543 orient: str | None = ..., 

544 typ: Literal["frame"] = ..., 

545 dtype: DtypeArg | None = ..., 

546 convert_axes: bool | None = ..., 

547 convert_dates: bool | list[str] | lib.NoDefault = ..., 

548 keep_default_dates: bool | lib.NoDefault = ..., 

549 precise_float: bool = ..., 

550 date_unit: str | None = ..., 

551 encoding: str | None = ..., 

552 encoding_errors: str | None = ..., 

553 lines: bool = ..., 

554 chunksize: int, 

555 compression: CompressionOptions = ..., 

556 nrows: int | None = ..., 

557 storage_options: StorageOptions = ..., 

558 dtype_backend: DtypeBackend | lib.NoDefault = ..., 

559 engine: JSONEngine = ..., 

560) -> JsonReader[Literal["frame"]]: ... 

561 

562 

563@overload 

564def read_json( 

565 path_or_buf: FilePath | ReadBuffer[str] | ReadBuffer[bytes], 

566 *, 

567 orient: str | None = ..., 

568 typ: Literal["series"], 

569 dtype: DtypeArg | None = ..., 

570 convert_axes: bool | None = ..., 

571 convert_dates: bool | list[str] | lib.NoDefault = ..., 

572 keep_default_dates: bool | lib.NoDefault = ..., 

573 precise_float: bool = ..., 

574 date_unit: str | None = ..., 

575 encoding: str | None = ..., 

576 encoding_errors: str | None = ..., 

577 lines: bool = ..., 

578 chunksize: int, 

579 compression: CompressionOptions = ..., 

580 nrows: int | None = ..., 

581 storage_options: StorageOptions = ..., 

582 dtype_backend: DtypeBackend | lib.NoDefault = ..., 

583 engine: JSONEngine = ..., 

584) -> JsonReader[Literal["series"]]: ... 

585 

586 

587@overload 

588def read_json( 

589 path_or_buf: FilePath | ReadBuffer[str] | ReadBuffer[bytes], 

590 *, 

591 orient: str | None = ..., 

592 typ: Literal["series"], 

593 dtype: DtypeArg | None = ..., 

594 convert_axes: bool | None = ..., 

595 convert_dates: bool | list[str] | lib.NoDefault = ..., 

596 keep_default_dates: bool | lib.NoDefault = ..., 

597 precise_float: bool = ..., 

598 date_unit: str | None = ..., 

599 encoding: str | None = ..., 

600 encoding_errors: str | None = ..., 

601 lines: bool = ..., 

602 chunksize: None = ..., 

603 compression: CompressionOptions = ..., 

604 nrows: int | None = ..., 

605 storage_options: StorageOptions = ..., 

606 dtype_backend: DtypeBackend | lib.NoDefault = ..., 

607 engine: JSONEngine = ..., 

608) -> Series: ... 

609 

610 

611@overload 

612def read_json( 

613 path_or_buf: FilePath | ReadBuffer[str] | ReadBuffer[bytes], 

614 *, 

615 orient: str | None = ..., 

616 typ: Literal["frame"] = ..., 

617 dtype: DtypeArg | None = ..., 

618 convert_axes: bool | None = ..., 

619 convert_dates: bool | list[str] | lib.NoDefault = ..., 

620 keep_default_dates: bool | lib.NoDefault = ..., 

621 precise_float: bool = ..., 

622 date_unit: str | None = ..., 

623 encoding: str | None = ..., 

624 encoding_errors: str | None = ..., 

625 lines: bool = ..., 

626 chunksize: None = ..., 

627 compression: CompressionOptions = ..., 

628 nrows: int | None = ..., 

629 storage_options: StorageOptions = ..., 

630 dtype_backend: DtypeBackend | lib.NoDefault = ..., 

631 engine: JSONEngine = ..., 

632) -> DataFrame: ... 

633 

634 

635@set_module("pandas") 

636def read_json( 

637 path_or_buf: FilePath | ReadBuffer[str] | ReadBuffer[bytes], 

638 *, 

639 orient: str | None = None, 

640 typ: Literal["frame", "series"] = "frame", 

641 dtype: DtypeArg | None = None, 

642 convert_axes: bool | None = None, 

643 convert_dates: bool | list[str] | lib.NoDefault = lib.no_default, 

644 keep_default_dates: bool | lib.NoDefault = lib.no_default, 

645 precise_float: bool = False, 

646 date_unit: str | None = None, 

647 encoding: str | None = None, 

648 encoding_errors: str | None = "strict", 

649 lines: bool = False, 

650 chunksize: int | None = None, 

651 compression: CompressionOptions = "infer", 

652 nrows: int | None = None, 

653 storage_options: StorageOptions | None = None, 

654 dtype_backend: DtypeBackend | lib.NoDefault = lib.no_default, 

655 engine: JSONEngine = "ujson", 

656) -> DataFrame | Series | JsonReader[Any]: 

657 """ 

658 Convert a JSON string to pandas object. 

659 

660 This method reads JSON files or JSON-like data and converts them into pandas 

661 objects. It supports a variety of input formats, including line-delimited JSON, 

662 compressed files, and various data representations (table, records, index-based, 

663 etc.). When `chunksize` is specified, an iterator is returned instead of loading 

664 the entire data into memory. 

665 

666 Parameters 

667 ---------- 

668 path_or_buf : a str path, path object or file-like object 

669 Any valid string path is acceptable. The string could be a URL. Valid 

670 URL schemes include http, ftp, s3, and file. For file URLs, a host is 

671 expected. A local file could be: 

672 ``file://localhost/path/to/table.json``. 

673 

674 Certain URL schemes may require additional packages. For example, S3 

675 URLs require the ``s3fs`` library. See 

676 :ref:`install.optional_dependencies` for a full list. 

677 

678 If you want to pass in a path object, pandas accepts any 

679 ``os.PathLike``. 

680 

681 By file-like object, we refer to objects with a ``read()`` method, 

682 such as a file handle (e.g. via builtin ``open`` function) 

683 or ``StringIO``. 

684 

685 orient : str, optional 

686 Indication of expected JSON string format. 

687 Compatible JSON strings can be produced by ``to_json()`` with a 

688 corresponding orient value. 

689 When ``typ == 'frame'``, the set of possible orients is: 

690 

691 - ``'split'`` : dict like 

692 ``{index -> [index], columns -> [columns], data -> [values]}`` 

693 - ``'records'`` : list like 

694 ``[{column -> value}, ... , {column -> value}]`` 

695 - ``'index'`` : dict like ``{index -> {column -> value}}`` 

696 - ``'columns'`` : dict like ``{column -> {index -> value}}`` 

697 - ``'values'`` : just the values array 

698 - ``'table'`` : dict like ``{'schema': {schema}, 'data': {data}}`` 

699 

700 When ``typ == 'series'``, the shapes differ, since a Series has no 

701 columns: 

702 

703 - ``'split'`` : dict like 

704 ``{name -> name, index -> [index], data -> [values]}`` 

705 - ``'records'`` : list like ``[value, ... , value]`` 

706 - ``'index'`` : dict like ``{index -> value}`` 

707 

708 The allowed and default values depend on the value 

709 of the `typ` parameter. 

710 

711 * when ``typ == 'series'``, 

712 

713 - allowed orients are ``{'split','records','index'}`` 

714 - default is ``'index'`` 

715 - The Series index must be unique for orient ``'index'``. 

716 

717 * when ``typ == 'frame'``, 

718 

719 - allowed orients are ``{'split','records','index', 

720 'columns','values', 'table'}`` 

721 - default is ``'columns'`` 

722 - The DataFrame index must be unique for orients ``'index'`` and 

723 ``'columns'``. 

724 - The DataFrame columns must be unique for orients ``'index'``, 

725 ``'columns'``, and ``'records'``. 

726 

727 typ : {'frame', 'series'}, default 'frame' 

728 The type of object to recover. 

729 

730 dtype : bool or dict, default None 

731 If True, infer dtypes; if a dict of column to dtype, then use those; 

732 if False, then don't infer dtypes at all, applies only to the data. 

733 

734 For all ``orient`` values except ``'table'``, default is True. 

735 

736 convert_axes : bool, default None 

737 Try to convert the axes to the proper dtypes. 

738 

739 For all ``orient`` values except ``'table'``, default is True. 

740 

741 convert_dates : bool or list of str, default True 

742 If True then default datelike columns may be converted (depending on 

743 keep_default_dates). 

744 If False, no dates will be converted. 

745 If a list of column names, then those columns will be converted and 

746 default datelike columns may also be converted (depending on 

747 keep_default_dates). 

748 

749 .. deprecated:: 3.1.0 

750 Pass ``dtype=False`` to disable type conversion, or parse date 

751 columns with :func:`~pandas.to_datetime` after reading. 

752 

753 keep_default_dates : bool, default True 

754 If parsing dates (convert_dates is not False), then try to parse the 

755 default datelike columns. 

756 A column label is datelike if 

757 

758 * it ends with ``'_at'``, 

759 

760 * it ends with ``'_time'``, 

761 

762 * it begins with ``'timestamp'``, 

763 

764 * it is ``'modified'``, or 

765 

766 * it is ``'date'``. 

767 

768 .. deprecated:: 3.1.0 

769 Pass ``dtype=False`` to disable type conversion, or parse date 

770 columns with :func:`~pandas.to_datetime` after reading. 

771 

772 precise_float : bool, default False 

773 Set to enable usage of higher precision (strtod) function when 

774 decoding string to double values. Default (False) is to use fast but 

775 less precise builtin functionality. 

776 

777 date_unit : str, default None 

778 The timestamp unit to detect if converting dates. The default behaviour 

779 is to try and detect the correct precision, but if this is not desired 

780 then pass one of 's', 'ms', 'us' or 'ns' to force parsing only seconds, 

781 milliseconds, microseconds or nanoseconds respectively. 

782 

783 encoding : str, default is 'utf-8' 

784 The encoding to use to decode py3 bytes. 

785 

786 encoding_errors : str, optional, default "strict" 

787 How encoding errors are treated. `List of possible values 

788 <https://docs.python.org/3/library/codecs.html#error-handlers>`_ . 

789 

790 lines : bool, default False 

791 Read the file as a json object per line. 

792 

793 chunksize : int, optional 

794 Return JsonReader object for iteration. 

795 See the `line-delimited json docs 

796 <https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html#line-delimited-json>`_ 

797 for more information on ``chunksize``. 

798 This can only be passed if `lines=True`. 

799 If this is None, the file will be read into memory all at once. 

800 

801 compression : str or dict, default 'infer' 

802 For on-the-fly decompression of on-disk data. If 'infer' and 'path_or_buf' is 

803 path-like, then detect compression from the following extensions: '.gz', 

804 '.bz2', '.zip', '.xz', '.zst', '.tar', '.tar.gz', '.tar.xz' or '.tar.bz2' 

805 (otherwise no compression). 

806 If using 'zip' or 'tar', the ZIP file must contain only one data file to be 

807 read in. 

808 Set to ``None`` for no decompression. 

809 Can also be a dict with key ``'method'`` set 

810 to one of {``'zip'``, ``'gzip'``, ``'bz2'``, ``'zstd'``, ``'xz'``, ``'tar'``} 

811 and other key-value pairs are forwarded to 

812 ``zipfile.ZipFile``, ``gzip.GzipFile``, 

813 ``bz2.BZ2File``, ``zstandard.ZstdDecompressor``, ``lzma.LZMAFile`` or 

814 ``tarfile.TarFile``, respectively. 

815 As an example, the following could be passed for Zstandard decompression using a 

816 custom compression dictionary: 

817 ``compression={'method': 'zstd', 'dict_data': my_compression_dict}``. 

818 

819 nrows : int, optional 

820 The number of lines from the line-delimited jsonfile that has to be read. 

821 This can only be passed if `lines=True`. 

822 If this is None, all the rows will be returned. 

823 

824 storage_options : dict, optional 

825 Extra options that make sense for a particular storage connection, e.g. 

826 host, port, username, password, etc. For HTTP(S) URLs the key-value pairs 

827 are forwarded to ``urllib.request.Request`` as header options. For other 

828 URLs (e.g. starting with "s3://", and "gcs://") the key-value pairs are 

829 forwarded to ``fsspec.open``. Please see ``fsspec`` and ``urllib`` for more 

830 details, and for more examples on storage options refer `here 

831 <https://pandas.pydata.org/docs/user_guide/io.html? 

832 highlight=storage_options#reading-writing-remote-files>`_. 

833 

834 dtype_backend : {'numpy_nullable', 'pyarrow'} 

835 Back-end data type applied to the resultant :class:`DataFrame` 

836 (still experimental). If not specified, the default behavior 

837 is to not use nullable data types. If specified, the behavior 

838 is as follows: 

839 

840 * ``"numpy_nullable"``: returns nullable-dtype-backed :class:`DataFrame` 

841 * ``"pyarrow"``: returns pyarrow-backed nullable 

842 :class:`ArrowDtype` :class:`DataFrame` 

843 

844 .. versionadded:: 2.0 

845 

846 engine : {"ujson", "pyarrow"}, default "ujson" 

847 Parser engine to use. The ``"pyarrow"`` engine is only available when 

848 ``lines=True``, and only supports ``typ="frame"`` with default parsing 

849 options; passing an option it cannot apply (for example ``convert_dates``, 

850 ``date_unit``, ``precise_float``, ``encoding``, or a non-``ArrowDtype`` 

851 ``dtype``) raises ``ValueError``. 

852 

853 .. versionadded:: 2.0 

854 

855 Returns 

856 ------- 

857 Series, DataFrame, or pandas.api.typing.JsonReader 

858 A JsonReader is returned when ``chunksize`` is not ``0`` or ``None``. 

859 Otherwise, the type returned depends on the value of ``typ``. 

860 

861 See Also 

862 -------- 

863 DataFrame.to_json : Convert a DataFrame to a JSON string. 

864 Series.to_json : Convert a Series to a JSON string. 

865 json_normalize : Normalize semi-structured JSON data into a flat table. 

866 

867 Notes 

868 ----- 

869 Specific to ``orient='table'``, if a :class:`DataFrame` with a literal 

870 :class:`Index` name of `index` gets written with :func:`to_json`, the 

871 subsequent read operation will incorrectly set the :class:`Index` name to 

872 ``None``. This is because `index` is also used by :func:`DataFrame.to_json` 

873 to denote a missing :class:`Index` name, and the subsequent 

874 :func:`read_json` operation cannot distinguish between the two. The same 

875 limitation is encountered with a :class:`MultiIndex` and any names 

876 beginning with ``'level_'``. 

877 

878 Examples 

879 -------- 

880 >>> from io import StringIO 

881 >>> df = pd.DataFrame( 

882 ... [["a", "b"], ["c", "d"]], 

883 ... index=["row 1", "row 2"], 

884 ... columns=["col 1", "col 2"], 

885 ... ) 

886 

887 Encoding/decoding a Dataframe using ``'split'`` formatted JSON: 

888 

889 >>> df.to_json(orient="split") 

890 '{"columns":["col 1","col 2"],"index":["row 1","row 2"],"data":[["a","b"],["c","d"]]}' 

891 

892 >>> pd.read_json(StringIO(_), orient="split") # noqa: F821 

893 col 1 col 2 

894 row 1 a b 

895 row 2 c d 

896 

897 Encoding/decoding a Dataframe using ``'index'`` formatted JSON: 

898 

899 >>> df.to_json(orient="index") 

900 '{"row 1":{"col 1":"a","col 2":"b"},"row 2":{"col 1":"c","col 2":"d"}}' 

901 

902 >>> pd.read_json(StringIO(_), orient="index") # noqa: F821 

903 col 1 col 2 

904 row 1 a b 

905 row 2 c d 

906 

907 Encoding/decoding a Dataframe using ``'records'`` formatted JSON. 

908 Note that index labels are not preserved with this encoding. 

909 

910 >>> df.to_json(orient="records") 

911 '[{"col 1":"a","col 2":"b"},{"col 1":"c","col 2":"d"}]' 

912 

913 >>> pd.read_json(StringIO(_), orient="records") # noqa: F821 

914 col 1 col 2 

915 0 a b 

916 1 c d 

917 

918 Encoding with Table Schema 

919 

920 >>> df.to_json(orient="table") 

921 '{"schema":{"fields":[{"name":"index","type":"string","extDtype":"str"},{"name":"col 1","type":"string","extDtype":"str"},{"name":"col 2","type":"string","extDtype":"str"}],"primaryKey":["index"],"pandas_version":"1.4.0"},"data":[{"index":"row 1","col 1":"a","col 2":"b"},{"index":"row 2","col 1":"c","col 2":"d"}]}' 

922 

923 The following example uses ``dtype_backend="numpy_nullable"`` 

924 

925 >>> data = '''{"index": {"0": 0, "1": 1}, 

926 ... "a": {"0": 1, "1": null}, 

927 ... "b": {"0": 2.5, "1": 4.5}, 

928 ... "c": {"0": true, "1": false}, 

929 ... "d": {"0": "a", "1": "b"}, 

930 ... "e": {"0": 1577.2, "1": 1577.1}}''' 

931 >>> pd.read_json(StringIO(data), dtype_backend="numpy_nullable") 

932 index a b c d e 

933 0 0 1 2.5 True a 1577.2 

934 1 1 <NA> 4.5 False b 1577.1 

935 """ # noqa: E501 

936 if orient == "table" and dtype: 

937 raise ValueError("cannot pass both dtype and orient='table'") 

938 if orient == "table" and convert_axes: 

939 raise ValueError("cannot pass both convert_axes and orient='table'") 

940 

941 check_dtype_backend(dtype_backend) 

942 

943 if engine == "pyarrow": 

944 # Validate before dtype/convert_axes are resolved from None to True 

945 # below, so an explicitly-passed value can be told apart from the 

946 # default. 

947 _validate_pyarrow_engine_options( 

948 typ=typ, 

949 dtype=dtype, 

950 convert_axes=convert_axes, 

951 convert_dates=convert_dates, 

952 keep_default_dates=keep_default_dates, 

953 precise_float=precise_float, 

954 date_unit=date_unit, 

955 encoding=encoding, 

956 encoding_errors=encoding_errors, 

957 compression=compression, 

958 storage_options=storage_options, 

959 ) 

960 

961 if convert_dates is not lib.no_default: 

962 warnings.warn( 

963 "The 'convert_dates' keyword in read_json is deprecated and will be " 

964 "removed in a future version. Pass dtype=False to disable type " 

965 "conversion, or parse date columns with pd.to_datetime after reading.", 

966 Pandas4Warning, 

967 stacklevel=find_stack_level(), 

968 ) 

969 else: 

970 convert_dates = True 

971 

972 if keep_default_dates is not lib.no_default: 

973 warnings.warn( 

974 "The 'keep_default_dates' keyword in read_json is deprecated and will " 

975 "be removed in a future version. Pass dtype=False to disable type " 

976 "conversion, or parse date columns with pd.to_datetime after reading.", 

977 Pandas4Warning, 

978 stacklevel=find_stack_level(), 

979 ) 

980 else: 

981 keep_default_dates = True 

982 

983 if dtype is None and orient != "table": 

984 # error: Incompatible types in assignment (expression has type "bool", variable 

985 # has type "Union[ExtensionDtype, str, dtype[Any], Type[str], Type[float], 

986 # Type[int], Type[complex], Type[bool], Type[object], Dict[Hashable, 

987 # Union[ExtensionDtype, Union[str, dtype[Any]], Type[str], Type[float], 

988 # Type[int], Type[complex], Type[bool], Type[object]]], None]") 

989 dtype = True # type: ignore[assignment] 

990 if convert_axes is None and orient != "table": 

991 convert_axes = True 

992 

993 json_reader = JsonReader( 

994 path_or_buf, 

995 orient=orient, 

996 typ=typ, 

997 dtype=dtype, 

998 convert_axes=convert_axes, 

999 convert_dates=convert_dates, 

1000 keep_default_dates=keep_default_dates, 

1001 precise_float=precise_float, 

1002 date_unit=date_unit, 

1003 encoding=encoding, 

1004 lines=lines, 

1005 chunksize=chunksize, 

1006 compression=compression, 

1007 nrows=nrows, 

1008 storage_options=storage_options, 

1009 encoding_errors=encoding_errors, 

1010 dtype_backend=dtype_backend, 

1011 engine=engine, 

1012 ) 

1013 

1014 if chunksize: 

1015 return json_reader 

1016 else: 

1017 return json_reader.read() 

1018 

1019 

1020@set_module("pandas.api.typing") 

1021class JsonReader(abc.Iterator[DataFrame | Series], Generic[FrameSeriesStrT]): 

1022 """ 

1023 JsonReader provides an interface for reading in a JSON file. 

1024 

1025 If initialized with ``lines=True`` and ``chunksize``, can be iterated over 

1026 ``chunksize`` lines at a time. Otherwise, calling ``read`` reads in the 

1027 whole document. 

1028 

1029 A ``JsonReader`` is returned by :func:`~pandas.read_json` when 

1030 ``chunksize`` is passed; it is not usually instantiated directly. 

1031 

1032 Parameters 

1033 ---------- 

1034 filepath_or_buffer : a str path, path object or file-like object 

1035 Any valid string path is acceptable. The string could be a URL. Valid 

1036 URL schemes include http, ftp, s3, and file. pandas accepts any 

1037 ``os.PathLike``, or file-like object with a ``read()`` method, such 

1038 as a file handle or ``StringIO``. 

1039 orient : str 

1040 Indication of expected JSON string format. Compatible JSON strings 

1041 can be produced by ``to_json()`` with a corresponding orient value. 

1042 See :func:`~pandas.read_json` for the set of possible orients. 

1043 typ : {"frame", "series"} 

1044 The type of object to recover. 

1045 dtype : bool or dict 

1046 If True, infer dtypes; if a dict of column to dtype, then use those; 

1047 if False, then don't infer dtypes at all, applies only to the data. 

1048 convert_axes : bool or None 

1049 Try to convert the axes to the proper dtypes. 

1050 convert_dates : bool or list of str 

1051 If True then default datelike columns may be converted (depending on 

1052 ``keep_default_dates``). If False, no dates will be converted. If a 

1053 list of column names, then those columns will be converted and 

1054 default datelike columns may also be converted (depending on 

1055 ``keep_default_dates``). 

1056 keep_default_dates : bool 

1057 If parsing dates (``convert_dates`` is not False), then try to parse 

1058 the default datelike columns. See :func:`~pandas.read_json` for the 

1059 criteria used to determine whether a column label is datelike. 

1060 precise_float : bool 

1061 Set to enable usage of higher precision (strtod) function when 

1062 decoding string to double values. 

1063 date_unit : str or None 

1064 The timestamp unit to detect if converting dates. The default 

1065 behaviour is to try and detect the correct precision, but if this 

1066 is not desired then pass one of 's', 'ms', 'us' or 'ns' to force 

1067 parsing only seconds, milliseconds, microseconds or nanoseconds 

1068 respectively. 

1069 encoding : str or None 

1070 The encoding to use to decode py3 bytes. 

1071 lines : bool 

1072 Read the file as a json object per line. 

1073 chunksize : int or None 

1074 Return a ``JsonReader`` for iteration, yielding ``chunksize`` lines 

1075 at a time. Can only be passed if ``lines=True``. If this is None, 

1076 the file will be read into memory all at once. 

1077 compression : str or dict 

1078 For on-the-fly decompression of on-disk data, see 

1079 :func:`~pandas.read_json` for the accepted values. 

1080 nrows : int or None 

1081 The number of lines from the line-delimited json file that has to 

1082 be read. Can only be passed if ``lines=True``. If this is None, all 

1083 the rows will be returned. 

1084 storage_options : dict, optional 

1085 Extra options that make sense for a particular storage connection, 

1086 e.g. host, port, username, password, etc. See 

1087 :func:`~pandas.read_json` for more details. 

1088 encoding_errors : str, optional, default "strict" 

1089 How encoding errors are treated. `List of possible values 

1090 <https://docs.python.org/3/library/codecs.html#error-handlers>`_ . 

1091 dtype_backend : {"numpy_nullable", "pyarrow"} 

1092 Back-end data type applied to the resultant :class:`DataFrame` 

1093 (still experimental). See :func:`~pandas.read_json` for more 

1094 details. 

1095 engine : {"ujson", "pyarrow"}, default "ujson" 

1096 Parser engine to use. See :func:`~pandas.read_json` for the 

1097 restrictions on the ``"pyarrow"`` engine. 

1098 

1099 See Also 

1100 -------- 

1101 read_json : Convert a JSON string to pandas object. 

1102 

1103 Examples 

1104 -------- 

1105 >>> from io import StringIO 

1106 >>> df = pd.DataFrame({"a": [1, 2], "b": [3, 4]}) 

1107 >>> reader = pd.read_json( 

1108 ... StringIO(df.to_json(orient="records", lines=True)), 

1109 ... lines=True, 

1110 ... chunksize=1, 

1111 ... ) 

1112 >>> for chunk in reader: 

1113 ... print(chunk) 

1114 a b 

1115 0 1 3 

1116 a b 

1117 1 2 4 

1118 """ 

1119 

1120 def __init__( 

1121 self, 

1122 filepath_or_buffer, 

1123 orient, 

1124 typ: FrameSeriesStrT, 

1125 dtype, 

1126 convert_axes: bool | None, 

1127 convert_dates, 

1128 keep_default_dates: bool, 

1129 precise_float: bool, 

1130 date_unit, 

1131 encoding, 

1132 lines: bool, 

1133 chunksize: int | None, 

1134 compression: CompressionOptions, 

1135 nrows: int | None, 

1136 storage_options: StorageOptions | None = None, 

1137 encoding_errors: str | None = "strict", 

1138 dtype_backend: DtypeBackend | lib.NoDefault = lib.no_default, 

1139 engine: JSONEngine = "ujson", 

1140 ) -> None: 

1141 self.orient = orient 

1142 self.typ = typ 

1143 self.dtype = dtype 

1144 self.convert_axes = convert_axes 

1145 self.convert_dates = convert_dates 

1146 self.keep_default_dates = keep_default_dates 

1147 self.precise_float = precise_float 

1148 self.date_unit = date_unit 

1149 self.encoding = encoding 

1150 self.engine = engine 

1151 self.compression = compression 

1152 self.storage_options = storage_options 

1153 self.lines = lines 

1154 self.chunksize = chunksize 

1155 self.nrows_seen = 0 

1156 self.nrows = nrows 

1157 self.encoding_errors = encoding_errors 

1158 self.handles: IOHandles[str] | None = None 

1159 self.dtype_backend = dtype_backend 

1160 

1161 if self.engine not in {"pyarrow", "ujson"}: 

1162 raise ValueError( 

1163 f"The engine type {self.engine} is currently not supported." 

1164 ) 

1165 if self.chunksize is not None: 

1166 self.chunksize = validate_integer("chunksize", self.chunksize, 1) 

1167 if not self.lines: 

1168 raise ValueError("chunksize can only be passed if lines=True") 

1169 if self.engine == "pyarrow": 

1170 raise ValueError( 

1171 "currently pyarrow engine doesn't support chunksize parameter" 

1172 ) 

1173 if self.nrows is not None: 

1174 self.nrows = validate_integer("nrows", self.nrows, 0) 

1175 if not self.lines: 

1176 raise ValueError("nrows can only be passed if lines=True") 

1177 if self.engine == "pyarrow": 

1178 raise NotImplementedError( 

1179 "currently pyarrow engine doesn't support nrows parameter" 

1180 ) 

1181 if self.engine == "pyarrow": 

1182 if not self.lines: 

1183 raise ValueError( 

1184 "currently pyarrow engine only supports " 

1185 "the line-delimited JSON format" 

1186 ) 

1187 self.data = filepath_or_buffer 

1188 elif self.engine == "ujson": 

1189 data = self._get_data_from_filepath(filepath_or_buffer) 

1190 # If self.chunksize, we prepare the data for the `__next__` method. 

1191 # Otherwise, we read it into memory for the `read` method. 

1192 if self.chunksize is None and self.nrows is None: 

1193 with self: 

1194 self.data = data.read() 

1195 else: 

1196 self.data = data 

1197 

1198 def _get_data_from_filepath(self, filepath_or_buffer): 

1199 """ 

1200 The function read_json accepts three input types: 

1201 1. filepath (string-like) 

1202 2. file-like object (e.g. open file object, StringIO) 

1203 """ 

1204 filepath_or_buffer = stringify_path(filepath_or_buffer) 

1205 self.handles = get_handle( 

1206 filepath_or_buffer, 

1207 "r", 

1208 encoding=self.encoding, 

1209 compression=self.compression, 

1210 storage_options=self.storage_options, 

1211 errors=self.encoding_errors, 

1212 ) 

1213 filepath_or_buffer = self.handles.handle 

1214 return filepath_or_buffer 

1215 

1216 def _combine_lines(self, lines) -> str: 

1217 """ 

1218 Combines a list of JSON objects into one JSON object. 

1219 """ 

1220 return ( 

1221 f"[{','.join([line for line in (line.strip() for line in lines) if line])}]" 

1222 ) 

1223 

1224 @overload 

1225 def read(self: JsonReader[Literal["frame"]]) -> DataFrame: ... 

1226 

1227 @overload 

1228 def read(self: JsonReader[Literal["series"]]) -> Series: ... 

1229 

1230 @overload 

1231 def read(self: JsonReader[Literal["frame", "series"]]) -> DataFrame | Series: ... 

1232 

1233 def read(self) -> DataFrame | Series: 

1234 """ 

1235 Read the whole JSON input into a pandas object. 

1236 

1237 Unlike iterating over the reader, this reads the entire underlying 

1238 JSON input in a single call, regardless of whether ``chunksize`` 

1239 was specified when this reader was created. 

1240 

1241 Returns 

1242 ------- 

1243 DataFrame or Series 

1244 The parsed pandas object, depending on the ``typ`` passed when 

1245 this reader was created. 

1246 

1247 See Also 

1248 -------- 

1249 read_json : Convert a JSON string to pandas object. 

1250 

1251 Examples 

1252 -------- 

1253 >>> from io import StringIO 

1254 >>> df = pd.DataFrame({"a": [1, 2], "b": [3, 4]}) 

1255 >>> reader = pd.read_json( 

1256 ... StringIO(df.to_json(orient="records", lines=True)), 

1257 ... lines=True, 

1258 ... chunksize=2, 

1259 ... ) 

1260 >>> reader.read() 

1261 a b 

1262 0 1 3 

1263 1 2 4 

1264 """ 

1265 obj: DataFrame | Series 

1266 with self: 

1267 if self.engine == "pyarrow": 

1268 obj = self._read_pyarrow() 

1269 elif self.engine == "ujson": 

1270 obj = self._read_ujson() 

1271 

1272 return obj 

1273 

1274 def _read_pyarrow(self) -> DataFrame: 

1275 """ 

1276 Read JSON using the pyarrow engine. 

1277 """ 

1278 pyarrow_json = import_optional_dependency("pyarrow.json") 

1279 options = None 

1280 

1281 if isinstance(self.dtype, dict): 

1282 pa = import_optional_dependency("pyarrow") 

1283 fields = [] 

1284 for field, dtype in self.dtype.items(): 

1285 pd_dtype = pandas_dtype(dtype) 

1286 if isinstance(pd_dtype, ArrowDtype): 

1287 fields.append((field, pd_dtype.pyarrow_dtype)) 

1288 

1289 schema = pa.schema(fields) 

1290 options = pyarrow_json.ParseOptions( 

1291 explicit_schema=schema, unexpected_field_behavior="infer" 

1292 ) 

1293 

1294 pa_table = pyarrow_json.read_json(self.data, parse_options=options) 

1295 df = arrow_table_to_pandas(pa_table, dtype_backend=self.dtype_backend) 

1296 

1297 return df 

1298 

1299 def _read_ujson(self) -> DataFrame | Series: 

1300 """ 

1301 Read JSON using the ujson engine. 

1302 """ 

1303 obj: DataFrame | Series 

1304 if self.lines: 

1305 if self.chunksize: 

1306 chunks = list(self) 

1307 if chunks: 

1308 obj = concat(chunks) 

1309 else: 

1310 obj = self._get_object_parser(self._combine_lines([])) 

1311 elif self.nrows is not None: 

1312 lines = list(islice(self.data, self.nrows)) 

1313 lines_json = self._combine_lines(lines) 

1314 obj = self._get_object_parser(lines_json) 

1315 else: 

1316 data = ensure_str(self.data) 

1317 data_lines = data.split("\n") 

1318 obj = self._get_object_parser(self._combine_lines(data_lines)) 

1319 else: 

1320 obj = self._get_object_parser(self.data) 

1321 if self.dtype_backend is not lib.no_default: 

1322 with option_context("future.distinguish_nan_and_na", False): 

1323 return obj.convert_dtypes(dtype_backend=self.dtype_backend) 

1324 else: 

1325 return obj 

1326 

1327 def _get_object_parser(self, json: str) -> DataFrame | Series: 

1328 """ 

1329 Parses a json document into a pandas object. 

1330 """ 

1331 typ = self.typ 

1332 dtype = self.dtype 

1333 kwargs = { 

1334 "orient": self.orient, 

1335 "dtype": self.dtype, 

1336 "convert_axes": self.convert_axes, 

1337 "convert_dates": self.convert_dates, 

1338 "keep_default_dates": self.keep_default_dates, 

1339 "precise_float": self.precise_float, 

1340 "date_unit": self.date_unit, 

1341 "dtype_backend": self.dtype_backend, 

1342 } 

1343 if typ == "frame": 

1344 return FrameParser(json, **kwargs).parse() 

1345 elif typ == "series": 

1346 if not isinstance(dtype, bool): 

1347 kwargs["dtype"] = dtype 

1348 return SeriesParser(json, **kwargs).parse() 

1349 else: 

1350 raise ValueError(f"{typ=} must be 'frame' or 'series'.") 

1351 

1352 def close(self) -> None: 

1353 """ 

1354 Close the underlying file handle, if one was opened by the reader. 

1355 

1356 If an open stream or file-like object was passed to the reader 

1357 rather than a path, it is left open and this method does not 

1358 close it. 

1359 

1360 See Also 

1361 -------- 

1362 read_json : Convert a JSON string to pandas object. 

1363 

1364 Examples 

1365 -------- 

1366 >>> from io import StringIO 

1367 >>> df = pd.DataFrame({"a": [1, 2], "b": [3, 4]}) 

1368 >>> reader = pd.read_json( 

1369 ... StringIO(df.to_json(orient="records", lines=True)), 

1370 ... lines=True, 

1371 ... chunksize=1, 

1372 ... ) 

1373 >>> reader.close() 

1374 """ 

1375 if self.handles is not None: 

1376 self.handles.close() 

1377 

1378 def __iter__(self) -> Self: 

1379 return self 

1380 

1381 @overload 

1382 def __next__(self: JsonReader[Literal["frame"]]) -> DataFrame: ... 

1383 

1384 @overload 

1385 def __next__(self: JsonReader[Literal["series"]]) -> Series: ... 

1386 

1387 @overload 

1388 def __next__( 

1389 self: JsonReader[Literal["frame", "series"]], 

1390 ) -> DataFrame | Series: ... 

1391 

1392 def __next__(self) -> DataFrame | Series: 

1393 chunk_size: int | None 

1394 if self.nrows is not None: 

1395 remaining = self.nrows - self.nrows_seen 

1396 if remaining <= 0: 

1397 self.close() 

1398 raise StopIteration 

1399 if self.chunksize is None: 

1400 chunk_size = remaining 

1401 else: 

1402 chunk_size = min(self.chunksize, remaining) 

1403 else: 

1404 chunk_size = self.chunksize 

1405 

1406 lines = list(islice(self.data, chunk_size)) 

1407 if not lines: 

1408 self.close() 

1409 raise StopIteration 

1410 

1411 try: 

1412 lines_json = self._combine_lines(lines) 

1413 obj = self._get_object_parser(lines_json) 

1414 

1415 # Make sure that the returned objects have the right index. 

1416 obj.index = range(self.nrows_seen, self.nrows_seen + len(obj)) 

1417 self.nrows_seen += len(obj) 

1418 except Exception as ex: 

1419 self.close() 

1420 raise ex 

1421 

1422 if self.dtype_backend is not lib.no_default: 

1423 with option_context("future.distinguish_nan_and_na", False): 

1424 return obj.convert_dtypes(dtype_backend=self.dtype_backend) 

1425 else: 

1426 return obj 

1427 

1428 def __enter__(self) -> Self: 

1429 return self 

1430 

1431 def __exit__( 

1432 self, 

1433 exc_type: type[BaseException] | None, 

1434 exc_value: BaseException | None, 

1435 traceback: TracebackType | None, 

1436 ) -> None: 

1437 self.close() 

1438 

1439 

1440class Parser: 

1441 _split_keys: tuple[str, ...] 

1442 _default_orient: str 

1443 

1444 _STAMP_UNITS = ("s", "ms", "us", "ns") 

1445 _MIN_STAMPS = { 

1446 "s": 31536000, 

1447 "ms": 31536000000, 

1448 "us": 31536000000000, 

1449 "ns": 31536000000000000, 

1450 } 

1451 json: str 

1452 

1453 def __init__( 

1454 self, 

1455 json: str, 

1456 orient, 

1457 dtype: DtypeArg | None = None, 

1458 convert_axes: bool = True, 

1459 convert_dates: bool | list[str] = True, 

1460 keep_default_dates: bool = False, 

1461 precise_float: bool = False, 

1462 date_unit=None, 

1463 dtype_backend: DtypeBackend | lib.NoDefault = lib.no_default, 

1464 ) -> None: 

1465 self.json = json 

1466 

1467 if orient is None: 

1468 orient = self._default_orient 

1469 

1470 self.orient = orient 

1471 

1472 self.dtype = dtype 

1473 

1474 if date_unit is not None: 

1475 date_unit = date_unit.lower() 

1476 if date_unit not in self._STAMP_UNITS: 

1477 raise ValueError(f"date_unit must be one of {self._STAMP_UNITS}") 

1478 self.min_stamp = self._MIN_STAMPS[date_unit] 

1479 else: 

1480 self.min_stamp = self._MIN_STAMPS["s"] 

1481 

1482 self.precise_float = precise_float 

1483 self.convert_axes = convert_axes 

1484 self.convert_dates = convert_dates 

1485 self.date_unit = date_unit 

1486 self.keep_default_dates = keep_default_dates 

1487 self.dtype_backend = dtype_backend 

1488 

1489 @final 

1490 def check_keys_split(self, decoded: dict[str, Any]) -> None: 

1491 """ 

1492 Checks that dict has only the appropriate keys for orient='split'. 

1493 """ 

1494 bad_keys = set(decoded.keys()).difference(set(self._split_keys)) 

1495 if bad_keys: 

1496 bad_keys_joined = ", ".join(bad_keys) 

1497 raise ValueError(f"JSON data had unexpected key(s): {bad_keys_joined}") 

1498 

1499 @final 

1500 def parse(self) -> DataFrame | Series: 

1501 obj = self._parse() 

1502 

1503 if self.convert_axes: 

1504 obj = self._convert_axes(obj) 

1505 obj = self._try_convert_types(obj) 

1506 return obj 

1507 

1508 def _parse(self) -> DataFrame | Series: 

1509 raise AbstractMethodError(self) 

1510 

1511 @final 

1512 def _convert_axes(self, obj: DataFrame | Series) -> DataFrame | Series: 

1513 """ 

1514 Try to convert axes. 

1515 """ 

1516 for axis_name in obj._AXIS_ORDERS: 

1517 ax = obj._get_axis(axis_name) 

1518 ser = Series(ax, dtype=ax.dtype, copy=False) 

1519 new_ser, result = self._try_convert_data( 

1520 name=axis_name, 

1521 data=ser, 

1522 use_dtypes=False, 

1523 convert_dates=True, 

1524 is_axis=True, 

1525 ) 

1526 if result: 

1527 new_axis = Index(new_ser, dtype=new_ser.dtype, copy=False) 

1528 setattr(obj, axis_name, new_axis) 

1529 return obj 

1530 

1531 def _try_convert_types(self, obj): 

1532 raise AbstractMethodError(self) 

1533 

1534 @final 

1535 def _try_convert_data( 

1536 self, 

1537 name: Hashable, 

1538 data: Series, 

1539 use_dtypes: bool = True, 

1540 convert_dates: bool | list[str] = True, 

1541 is_axis: bool = False, 

1542 ) -> tuple[Series, bool]: 

1543 """ 

1544 Try to parse a Series into a column by inferring dtype. 

1545 """ 

1546 org_data = data 

1547 # don't try to coerce, unless a force conversion 

1548 if use_dtypes: 

1549 if not self.dtype: 

1550 if all(notna(data)): 

1551 return data, False 

1552 

1553 filled = data.fillna(np.nan) 

1554 

1555 return filled, True 

1556 # error: Non-overlapping identity check (left operand type: "ExtensionDtype 

1557 # | str | dtype[Any] | type[object] | Mapping[Hashable, ExtensionDtype 

1558 # | str | dtype[Any] | type[str] | type[complex] | type[bool] 

1559 # | type[object]]", right operand type: "Literal[True]") 

1560 elif self.dtype is True: # type: ignore[comparison-overlap] 

1561 pass 

1562 elif not _should_convert_dates( 

1563 convert_dates, self.keep_default_dates, name 

1564 ): 

1565 # convert_dates takes precedence over columns listed in dtypes 

1566 dtype = ( 

1567 self.dtype.get(name) if isinstance(self.dtype, dict) else self.dtype 

1568 ) 

1569 if dtype is not None: 

1570 try: 

1571 return data.astype(dtype), True 

1572 except (TypeError, ValueError): 

1573 return data, False 

1574 

1575 if convert_dates: 

1576 new_data = self._try_convert_to_date(data) 

1577 if new_data is not data: 

1578 return new_data, True 

1579 

1580 converted = False 

1581 if self.dtype_backend is not lib.no_default and not is_axis: 

1582 # Fall through for conversion later on 

1583 return data, True 

1584 elif is_string_dtype(data.dtype): 

1585 # try float 

1586 try: 

1587 data = data.astype("float64") 

1588 converted = True 

1589 except (TypeError, ValueError): 

1590 pass 

1591 

1592 if data.dtype.kind == "f" and data.dtype != "float64": 

1593 # coerce floats to 64 

1594 try: 

1595 data = data.astype("float64") 

1596 converted = True 

1597 except (TypeError, ValueError): 

1598 pass 

1599 

1600 # don't coerce 0-len data 

1601 if len(data) and data.dtype in ("float", "object"): 

1602 # coerce ints if we can 

1603 try: 

1604 new_data = org_data.astype("int64") 

1605 if (new_data == data).all(): 

1606 data = new_data 

1607 converted = True 

1608 except (TypeError, ValueError, OverflowError): 

1609 pass 

1610 

1611 if data.dtype == "int" and data.dtype != "int64": 

1612 # coerce ints to 64 

1613 try: 

1614 data = data.astype("int64") 

1615 converted = True 

1616 except (TypeError, ValueError): 

1617 pass 

1618 

1619 # if we have an index, we want to preserve dtypes 

1620 if name == "index" and len(data): 

1621 if self.orient == "split": 

1622 return data, False 

1623 

1624 return data, converted 

1625 

1626 @final 

1627 def _try_convert_to_date(self, data: Series) -> Series: 

1628 """ 

1629 Try to parse an ndarray like into a date column. 

1630 

1631 Try to coerce object in epoch/iso formats and integer/float in epoch 

1632 formats. 

1633 """ 

1634 # no conversion on empty 

1635 if not len(data): 

1636 return data 

1637 

1638 new_data = data 

1639 

1640 if new_data.dtype == "object" or new_data.dtype == "string": # noqa: PLR1714 

1641 try: 

1642 new_data = data.astype("int64") 

1643 except OverflowError: 

1644 return data 

1645 except (TypeError, ValueError): 

1646 pass 

1647 

1648 # ignore numbers that are out of range 

1649 if issubclass(new_data.dtype.type, np.number): 

1650 in_range = ( 

1651 isna(new_data._values) 

1652 | (new_data > self.min_stamp) 

1653 | (new_data._values == iNaT) 

1654 ) 

1655 if not in_range.all(): 

1656 return data 

1657 

1658 if new_data.dtype == "string": 

1659 for format in (None, "ISO8601", "mixed"): 

1660 converted = None 

1661 with warnings.catch_warnings(record=True) as record: 

1662 warnings.simplefilter("always") 

1663 try: 

1664 converted = to_datetime(new_data, errors="raise", format=format) 

1665 except Exception: 

1666 pass 

1667 if converted is None: 

1668 continue 

1669 if format != "ISO8601" and _is_default_filled(converted): 

1670 # only a strict ISO8601 parse proves a year-1 result came 

1671 # from the strings themselves rather than from dateutil's 

1672 # defaults, so keep looking 

1673 continue 

1674 _reemit_parse_warnings(record, ignore_user_warnings=True) 

1675 return converted 

1676 else: 

1677 # numeric or mixed objects 

1678 date_units = (self.date_unit,) if self.date_unit else self._STAMP_UNITS 

1679 converted = None 

1680 in_ns_bounds = False 

1681 for date_unit in date_units: 

1682 with warnings.catch_warnings(record=True) as record: 

1683 warnings.simplefilter("always") 

1684 try: 

1685 # In case of multiple possible units, infer the likely unit 

1686 # based on the first unit for which the parsed dates fit 

1687 # within the nanoseconds bounds 

1688 # -> do as_unit cast to ensure OutOfBounds error 

1689 converted = to_datetime( 

1690 new_data, errors="raise", unit=date_unit 

1691 ) 

1692 _ = converted.dt.as_unit("ns") 

1693 in_ns_bounds = True 

1694 except ( 

1695 OutOfBoundsDatetime, 

1696 ValueError, 

1697 OverflowError, 

1698 TypeError, 

1699 ): 

1700 pass 

1701 if converted is not None and in_ns_bounds: 

1702 _reemit_parse_warnings(record) 

1703 data = converted 

1704 break 

1705 else: 

1706 if converted is not None and ( 

1707 not _is_default_filled(converted) 

1708 or _parses_strict_iso8601(new_data) 

1709 ): 

1710 # all units failed to cast to ns (eg with mixed string / int) 

1711 # but to_datetime still returned a result -> use this this 

1712 # result (with the last unit) and re-emit any warning 

1713 _reemit_parse_warnings(record) 

1714 data = converted 

1715 return data 

1716 

1717 

1718def _is_default_filled(converted: Series) -> bool: 

1719 """ 

1720 Whether a speculative string date parse only succeeded by filling in defaults. 

1721 

1722 GH#63473: ``to_datetime`` falls back to ``dateutil`` for strings it cannot 

1723 match against a format, and ``dateutil`` takes the year absent from the 

1724 string from ``datetime(1, 1, 1)``. Strings like ``"Jan"`` or ``"1st"`` parse 

1725 that way, so accepting the result would silently turn ordinary labels into 

1726 year 1 timestamps. Strings carrying only a time of day are filled from 

1727 *today* instead and so are not caught here. 

1728 

1729 A year-1 result is genuine when the strings parse under a strict ISO8601 

1730 format, which rejects ``"Jan"`` outright; callers check that separately, 

1731 either by having attempted ``format="ISO8601"`` themselves or via 

1732 ``_parses_strict_iso8601``. 

1733 """ 

1734 if converted.dtype.kind != "M": 

1735 # defensive; the parses attempted above all give datetime64 

1736 return False 

1737 return bool((converted.dt.year == 1).any()) 

1738 

1739 

1740def _parses_strict_iso8601(data: Series) -> bool: 

1741 """ 

1742 Whether every entry of ``data`` parses under a strict ISO8601 format. 

1743 

1744 Used to tell a genuine out-of-nanosecond-bounds date apart from a 

1745 default-filled parse on the object-dtype path, which -- unlike the 

1746 string-dtype path -- does not attempt ``format="ISO8601"`` itself. 

1747 """ 

1748 with warnings.catch_warnings(): 

1749 # a speculative probe whose result is discarded; anything it emits 

1750 # would be about a parse the caller never adopts 

1751 warnings.simplefilter("ignore") 

1752 try: 

1753 to_datetime(data, errors="raise", format="ISO8601") 

1754 except Exception: 

1755 return False 

1756 return True 

1757 

1758 

1759def _reemit_parse_warnings( 

1760 record: list[warnings.WarningMessage], 

1761 *, 

1762 ignore_user_warnings: bool = False, 

1763) -> None: 

1764 """ 

1765 Re-emit the warnings captured from a date parse ``_try_convert_to_date`` may 

1766 discard. 

1767 

1768 GH#50907: it parses the same values several ways and keeps at most one 

1769 result. The quarterly-string deprecation asks the user to change a call, so 

1770 it may only be emitted for the parse whose result they actually receive. 

1771 Unrelated warnings keep the visibility they had before. 

1772 """ 

1773 for warning in record: 

1774 if ignore_user_warnings and issubclass(warning.category, UserWarning): 

1775 # "Could not infer format", incorrectly raised for non-date strings 

1776 continue 

1777 warnings.warn( 

1778 warning.message, 

1779 warning.category, 

1780 stacklevel=find_stack_level(), 

1781 ) 

1782 

1783 

1784class SeriesParser(Parser): 

1785 _default_orient = "index" 

1786 _split_keys = ("name", "index", "data") 

1787 

1788 def _parse(self) -> Series: 

1789 data = ujson_loads(self.json, precise_float=self.precise_float) 

1790 

1791 if self.orient == "split": 

1792 decoded = {str(k): v for k, v in data.items()} 

1793 self.check_keys_split(decoded) 

1794 return Series(**decoded) 

1795 else: 

1796 return Series(data) 

1797 

1798 def _try_convert_types(self, obj: Series) -> Series: 

1799 obj, _ = self._try_convert_data("data", obj, convert_dates=self.convert_dates) 

1800 return obj 

1801 

1802 

1803class FrameParser(Parser): 

1804 _default_orient = "columns" 

1805 _split_keys = ("columns", "index", "data") 

1806 

1807 def _parse(self) -> DataFrame: 

1808 json = self.json 

1809 orient = self.orient 

1810 

1811 if orient == "split": 

1812 decoded = { 

1813 str(k): v 

1814 for k, v in ujson_loads(json, precise_float=self.precise_float).items() 

1815 } 

1816 self.check_keys_split(decoded) 

1817 orig_names = [ 

1818 (tuple(col) if isinstance(col, list) else col) 

1819 for col in decoded["columns"] 

1820 ] 

1821 decoded["columns"] = dedup_names( 

1822 orig_names, 

1823 is_potential_multi_index(orig_names, None), 

1824 ) 

1825 return DataFrame(dtype=None, **decoded) 

1826 elif orient == "index": 

1827 return DataFrame.from_dict( 

1828 ujson_loads(json, precise_float=self.precise_float), 

1829 dtype=None, 

1830 orient="index", 

1831 ) 

1832 elif orient == "table": 

1833 return parse_table_schema(json, precise_float=self.precise_float) 

1834 else: 

1835 # includes orient == "columns" 

1836 return DataFrame( 

1837 ujson_loads(json, precise_float=self.precise_float), dtype=None 

1838 ) 

1839 

1840 def _try_convert_types(self, obj: DataFrame) -> DataFrame: 

1841 arrays = [] 

1842 for col_label, series in obj.items(): 

1843 result, _ = self._try_convert_data( 

1844 col_label, 

1845 series, 

1846 convert_dates=_should_convert_dates( 

1847 self.convert_dates, 

1848 keep_default_dates=self.keep_default_dates, 

1849 col=col_label, 

1850 ), 

1851 ) 

1852 arrays.append(result.array) 

1853 return DataFrame._from_arrays( 

1854 arrays, obj.columns, obj.index, verify_integrity=False 

1855 ) 

1856 

1857 

1858def _should_convert_dates( 

1859 convert_dates: bool | list[str], 

1860 keep_default_dates: bool, 

1861 col: Hashable, 

1862) -> bool: 

1863 """ 

1864 Return bool whether a DataFrame column should be cast to datetime. 

1865 """ 

1866 if convert_dates is False: 

1867 # convert_dates=True means follow keep_default_dates 

1868 return False 

1869 elif not isinstance(convert_dates, bool) and col in set(convert_dates): 

1870 return True 

1871 elif not keep_default_dates: 

1872 return False 

1873 elif not isinstance(col, str): 

1874 return False 

1875 col_lower = col.lower() 

1876 if ( 

1877 col_lower.endswith(("_at", "_time")) 

1878 or col_lower in {"modified", "date", "datetime"} 

1879 or col_lower.startswith("timestamp") 

1880 ): 

1881 return True 

1882 return False