Last active
June 10, 2024 06:42
-
-
Save spencerpogo/0538252ed4b82d65e59115075369d34d to your computer and use it in GitHub Desktop.
Converts JSON objects into nix (hackishly).
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
"""Converts JSON objects into nix (hackishly).""" | |
import sys | |
import json | |
INDENT = " " * 2 | |
def strip_comments(t): | |
# fixme: doesn't work if JSON strings contain // | |
return "\n".join(l.partition("//")[0] for l in t.split("\n")) | |
def indent(s): | |
return "\n".join(INDENT + i for i in s.split("\n")) | |
def nix_stringify(s): | |
# fixme: this doesn't handle string interpolation and possibly has more bugs | |
return json.dumps(s) | |
def sanitize_key(s): | |
if s and s.isalnum() and not s[0].isdigit(): | |
return s | |
return nix_stringify(s) | |
def flatten_obj_item(k, v): | |
keys = [k] | |
val = v | |
while isinstance(val, dict) and len(val) == 1: | |
k = next(iter(val.keys())) | |
keys.append(k) | |
val = val[k] | |
return keys, val | |
def fmt_object(obj, flatten): | |
fields = [] | |
for k, v in obj.items(): | |
if flatten: | |
keys, val = flatten_obj_item(k, v) | |
formatted_key = ".".join(sanitize_key(i) for i in keys) | |
else: | |
formatted_key = sanitize_key(k) | |
val = v | |
fields.append(f"{formatted_key} = {fmt_any(val, flatten)};") | |
return "{\n" + indent("\n".join(fields)) + "\n}" | |
def fmt_array(o, flatten): | |
body = indent("\n".join(fmt_any(i, flatten) for i in o)) | |
return f"[\n{body}\n]" | |
def fmt_any(o, flatten): | |
if isinstance(o, str) or isinstance(o, bool) or isinstance(o, int): | |
return json.dumps(o) | |
if isinstance(o, list): | |
return fmt_array(o, flatten) | |
if isinstance(o, dict): | |
return fmt_object(o, flatten) | |
raise TypeError(f"Unknown type {type(o)!r}") | |
def main(): | |
flatten = "--flatten" in sys.argv | |
args = [a for a in sys.argv[1:] if not a.startswith("--")] | |
if len(args) < 1: | |
print(f"Usage: {sys.argv[0]} [--flatten] <file.json>", file=sys.stderr) | |
sys.exit(1) | |
with open(args[0], "r") as f: | |
data = json.loads(strip_comments(f.read())) | |
print(fmt_any(data, flatten=flatten)) | |
if __name__ == "__main__": | |
main() |
thanks man so useful
You're welcome. Thanks for reaching out.
Here's a package for using in Nix :)
json2nix = pkgs.writeScriptBin "json2nix" ''
${pkgs.python3}/bin/python ${pkgs.fetchurl {
url = "https://gist.githubusercontent.com/Scoder12/0538252ed4b82d65e59115075369d34d/raw/e86d1d64d1373a497118beb1259dab149cea951d/json2nix.py";
hash = "sha256-ROUIrOrY9Mp1F3m+bVaT+m8ASh2Bgz8VrPyyrQf9UNQ=";
}} $@
'';
thank you. very nice. i hope nix builtins will get this one too :)
It has some inaccuracies imo, so needs a bit of improvement. It needless quotes some attribute names, and can't handle URLs at all.
Yes, this was a quick hack (as it says at the top) and it wasn't meant to be the definitive implementation or anything. Since it seems like theres demand I could make a better version
Either way, thanks. :)
@dzmitry-lahoda builtins can basically handle this already:
$ nix-instantiate --eval -E 'builtins.fromJSON (builtins.readFile ./test.json)'
{ a = { b = { c = 1; }; }; }
and can be piped through a formatter:
$ nix-instantiate --eval -E 'builtins.fromJSON (builtins.readFile ./test.json)' | nixfmt
{
a = {
b = {
c = 1;
};
};
}
#! /usr/bin/env bash
filename="$(basename "$0")-$(date +%s)-$(basename "$1")"
cat "$1" | sed -e 's;//.*$;;g' > /tmp/"$filename"
echo "$(nix eval --expr 'builtins.fromJSON (builtins.readFile /tmp/'"$filename"')' --impure | nixfmt)"
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example: