Last active
July 16, 2023 08:56
-
-
Save andreif/4109020cc854939fadaab1eda1009b26 to your computer and use it in GitHub Desktop.
Convert JSON to YAML in Python without any dependency.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Convert JSON to colorful YAML. | |
Example: | |
$ alias jaml="python .../jaml.py" | |
$ curl https://api.thecatapi.com/v1/images/search -s | jaml | |
License: MIT | |
""" | |
def dump(data, iter=False, color=False, indent=2): | |
indent = ' ' * indent | |
def clr(code, value): | |
if not color: | |
return value | |
return f'\033[{code}m{value}\033[0m' | |
def _dump(value): | |
if value is None: | |
yield clr('3;31', 'null') | |
elif isinstance(value, bool): | |
yield clr('3;31', str(value).lower()) | |
elif isinstance(value, (int, float)): | |
yield clr(34, str(value)) | |
elif isinstance(value, str): | |
if '\n' in value: | |
yield clr(3, '|') | |
for line in value.splitlines(): | |
yield clr(3, line) | |
else: | |
yield clr(3, value or "''") | |
elif isinstance(value, list): | |
if value: | |
for item in value: | |
for n, line in enumerate(_dump(item)): | |
yield (indent if n else clr(36, '- ')) + line | |
else: | |
yield '[]' | |
elif isinstance(value, dict): | |
if value: | |
for key, val in value.items(): | |
key = clr(32, str(key) + ':') | |
if isinstance(val, (list, dict)) and val: | |
yield key | |
for line in _dump(val): | |
yield indent + line | |
else: | |
for n, line in enumerate(_dump(val)): | |
yield (indent if n else key + ' ') + line | |
else: | |
yield '{}' | |
else: | |
raise NotImplementedError(type(value)) | |
generator = _dump(value=data) | |
if iter: | |
return generator | |
else: | |
return '\n'.join(list(generator)) | |
if __name__ == '__main__': | |
import json | |
import sys | |
if not sys.stdin.isatty(): | |
text = sys.stdin.read() | |
try: | |
data = json.loads(text) | |
except Exception: | |
print(text) | |
else: | |
print(dump(data, color=True)) |
Testing is done by converting JSON to YAML and back and comparing the result to the initial input:
import datetime
import json
import sys
import ruamel.yaml # pip install ruamel.yaml
import yaml # pip install PyYAML
import jaml
def default(x):
if isinstance(x, datetime.datetime):
return x.isoformat().replace('000+00:00', 'Z')
return repr(x)
def test(text):
data = json.loads(text)
v1 = json.dumps(data, indent=2, default=default)
print(jaml.dump(data, color=True))
yml = jaml.dump(data)
data = ruamel.yaml.YAML(typ='safe', pure=True).load(yml) # YAML 1.2
# data = yaml.safe_load(yml) # YAML 1.1
v2 = json.dumps(data, indent=2, default=default)
if v1 != v2:
print('v1:', v1)
print('v2:', v2)
print("DIFF", file=sys.stderr)
exit(1)
if __name__ == '__main__':
if not sys.stdin.isatty():
test(sys.stdin.read())
exit()
1 and test('''
{"a": {"b": {"c": {"d": 1}}}}
''')
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
TODO:
Support multiline strings