Skip to content

Instantly share code, notes, and snippets.

@shollingsworth
Last active April 18, 2022 15:12
Show Gist options
  • Save shollingsworth/dc774cb905cf107b3df515c0f53773d4 to your computer and use it in GitHub Desktop.
Save shollingsworth/dc774cb905cf107b3df515c0f53773d4 to your computer and use it in GitHub Desktop.
Convert JSON to python dataclass through stdin or file argument.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Convert JSON to python dataclass."""
import argparse
import json
import select
from string import Template
import sys
from typing import Any, Tuple
TEMPLATE = Template(
'''
$imports
# Setting init to false will ignore keys that are not in the dataclass, and
# pull them in via the constructor we define below.
@dataclass(init=False)
class $class_name:
def __init__(self, **kwargs) -> None:
"""$class_name DataClass."""
self.__dict__.update(kwargs)
# strip out Optional parameter for elements that are required exist
$data
'''.strip()
)
def get_type_string(value: Any) -> Tuple[str, Any, bool]:
"""Get type string."""
if isinstance(value, str):
return "str", "''", False
elif isinstance(value, int):
return "int", 'None', False
elif isinstance(value, float):
return "float", 'None', False
elif isinstance(value, bool):
return "bool", 'None', False
elif isinstance(value, list):
return "List", '[]', True
elif isinstance(value, dict):
return "Dict", 'field(default_factory=dict)', True
else:
return "Any", None, True
def run(args):
"""Run."""
print(args)
def main():
"""Run main function."""
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description=__doc__,
)
parser.add_argument(
"-f",
"--file",
help="JSON file",
type=str,
default=None,
)
args = parser.parse_args()
# args = parser.parse_args("-f ./test.txt".split())
if not args.file:
if select.select([sys.stdin], [], [], 0.0)[0]:
content = sys.stdin.read()
else:
sys.stderr.write("No input file or stdin detected\n")
parser.print_help()
sys.exit(1)
else:
with open(args.file, "r") as f:
content = f.read()
data = json.loads(content)
build_str = [" "]
imports = [
"from dataclasses import dataclass",
"from typing import Optional",
]
# from typing import List
for key, value in data.items():
type_str, def_val, is_type = get_type_string(value)
if is_type:
imports.append("from typing import {}".format(type_str))
build_str.append(f" {key}: Optional[{type_str}] = {def_val}")
build_str = "\n".join(build_str)
print(
TEMPLATE.substitute(
class_name="DataClass",
imports="\n".join(imports),
data=build_str,
)
)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment