Source code for domonic.JSON

"""
domonic.JSON
============

JSON and table-conversion helpers used throughout domonic.

This module focuses on practical conversions between Python objects, JSON
payloads, CSV data, and simple HTML table structures.
"""

from __future__ import annotations

import csv
import json
from collections.abc import Mapping, Sequence
from pathlib import Path
from typing import Any

from domonic.decorators import as_json
from domonic.html import table, td, th, tr

return_json = as_json  # legacy. use the one in decorators package


[docs] def parse_file(filepath: str | Path) -> Any: """[loads a json file and returns a python object] Args: filepath (str): [path to json file] Returns: [type]: [a python object] """ with open(filepath, encoding="utf-8") as json_file: return json.load(json_file)
[docs] def parse(json_string: str | bytes | bytearray) -> Any: """[take a json string and return a python object] Args: json_string (str): [a json string] Returns: [type]: [a python object] """ if isinstance(json_string, (bytes, bytearray)): json_string = json_string.decode("utf-8") return json.loads(json_string)
[docs] def stringify(data: Any, filepath: str | Path | None = None, **kwargs) -> str: """[stringify a python object] Args: data ([type]): [the python object] filepath (str, optional): [an optional filepath to save the stringified object] [default: None] Returns: [type]: [the stringified object] """ payload = json.dumps(data, **kwargs) if filepath is not None: with open(filepath, "w", encoding="utf-8") as json_file: json_file.write(payload) return payload
[docs] def tablify(arr: str | Mapping[str, Any] | Sequence[Mapping[str, Any]]): """tablify takes a json array and returns a html table Args: arr (list): the json array Returns: str: a html table """ def _get_headings(items, node): headings: list[str] = [] row = tr() for each in items: if not isinstance(each, Mapping): raise ValueError("tablify expects a dict or list of dicts") for key in each: if key not in headings: headings.append(key) row.appendChild(th(key)) node.appendChild(row) return headings if isinstance(arr, str): arr = json.loads(arr) if isinstance(arr, dict): arr = [arr] if not isinstance(arr, Sequence): raise ValueError("tablify expects a dict or list of dicts") t = table() headings = _get_headings(arr, t) for item in arr: row = tr(*[td(item.get(heading, "")) for heading in headings]) t.appendChild(row) return t
[docs] def table2json(node) -> list[dict[str, str]]: """Convert a domonic table node back into a list of row dictionaries.""" if getattr(node, "tagName", None) != "table": raise ValueError("table2json expects a domonic table element") rows = node.getElementsByTagName("tr") if not rows: return [] headings = [heading.textContent for heading in rows[0].getElementsByTagName("th")] items: list[dict[str, str]] = [] for row in rows[1:]: cells = row.getElementsByTagName("td") items.append( { heading: cells[index].textContent if index < len(cells) else "" for index, heading in enumerate(headings) } ) return items
[docs] def csvify(arr: str | Mapping[str, Any] | Sequence[Mapping[str, Any]], outfile: str | Path = "data.csv") -> str: """csvify takes a json array and dumps a csv file Args: arr (list): the json array outfile (list): the output file Returns: str: a csv file """ if isinstance(arr, str): arr = json.loads(arr) # leniency. allow for a string elif isinstance(arr, dict): arr = [arr] if not isinstance(arr, Sequence): raise ValueError("csvify expects a dict or list of dicts") def _get_headings(items): headings: list[str] = [] for each in items: if not isinstance(each, Mapping): raise ValueError("csvify expects a dict or list of dicts") for key in each: if key not in headings: headings.append(key) return headings headings = _get_headings(arr) with open(outfile, "w", encoding="utf-8", newline="") as file: output = csv.writer(file) output.writerow(headings) for row in arr: output.writerow([row.get(heading, "") for heading in headings]) return str(outfile)
[docs] def csv2json(csv_filepath: str | Path, json_filepath: str | Path | None = None) -> str: """ convert a CSV to JSON. """ items = [] with open(csv_filepath, encoding="utf-8", newline="") as csvf: csv_reader = csv.DictReader(csvf) for row in csv_reader: items.append(row) payload = json.dumps(items) if json_filepath is None: return payload with open(json_filepath, "w", encoding="utf-8") as json_file: json.dump(items, json_file, indent=4) return payload
""" def csv2json_hugefile(arr, infile="data.csv", start_row=0): def _load_data(csv_fname): with open(csv_fname, "r", encoding="latin-1") as records: for row in csv.reader(records): yield row items = iter(load_data(infile)) headings = next(companies) for i in range(start_row): next(companies) for item in items: # TODO - streamwrite to json file. """
[docs] def flatten(b: Mapping[str, Any], delim: str = "__") -> dict[str, Any]: """ # i.e. input = map( lambda x: JSON.flatten( x, "__" ), input ) """ val: dict[str, Any] = {} for i in b.keys(): if isinstance(b[i], dict): get = flatten(b[i], delim) for j in get.keys(): val[i + delim + j] = get[j] else: val[i] = b[i] return val
def is_json(value: str) -> bool: if not isinstance(value, str): return False value = value.strip() if not value: return False try: json.loads(value) except (TypeError, ValueError): return False return True