Edit: I found a solution using the htmlfile
COM object, which should offer the fastest performance (at least for a single run) and does not require an Internet connection. See the last solution in this answer.
Because you tagged this question with the [batch-file] tag, and because I found the challenge interesting, I wrote a hybrid batch + JScript script that'll beautify your JSON. Since JScript 5.7 doesn't natively support the JSON object, this script uses an external json2.js, downloading it via XHR if it's not already been downloaded. From there, it's a simple matter of calling JavaScript's familiar JSON.stringify()
method with its beautify options.
Syntax:
json_generator | batfile.bat
-or-
batfile.bat < jsonfile.json
Example usage:
beautify.bat < "C:\output\serviceName.json" > "C:\output\beautified.json"
This results in the following being saved as beautified.json:
{
"status": "success",
"user": "name",
"regId": "14420",
"subscriber": [
{
"memberFor": "3 years",
"lastLogin": "2 days ago"
}
]
}
The code:
@if (@CodeSection == @Batch) @then
@echo off & setlocal
cscript /nologo /e:JScript "%~f0"
goto :EOF
@end // end Batch / begin JScript hybrid chimera
var xObj = WSH.CreateObject('Microsoft.XMLHTTP'),
fso = WSH.CreateObject('Scripting.FileSystemObject'),
temp = WSH.CreateObject('WScript.Shell').Environment('Process')('temp'),
j2lib = 'https://raw.githubusercontent.com/douglascrockford/JSON-js/master/json2.js',
json = WSH.StdIn.ReadAll();
if (fso.FileExists(temp + '\\json2.js')) {
j2lib = fso.OpenTextFile(temp + '\\json2.js', 1);
eval(j2lib.ReadAll());
j2lib.Close();
}
else {
with (xObj) {
open("GET", j2lib, true);
setRequestHeader('User-Agent', 'XMLHTTP/1.0');
send('');
}
while (xObj.readyState != 4) WSH.Sleep(50);
eval(xObj.responseText);
j2lib = fso.CreateTextFile(temp + '\\json2.js', true);
j2lib.Write(xObj.responseText);
j2lib.Close();
}
WSH.Echo(JSON.stringify(JSON.parse(json), null, '\t'));
Here's another solution using the same syntax that does not require downloading json2.js. It avoids this by launching Internet Explorer invisibly, calling IE's built-in JSON methods, then silently closing IE again. This is most likely going to be slower than the method above, and it could be blocked depending on machine security policies; but it does have the advantage of working without an Internet connection.
@if (@CodeSection == @Batch) @then
@echo off & setlocal
cscript /nologo /e:JScript "%~f0"
goto :EOF
@end // end Batch / begin JScript hybrid chimera
var IE = WSH.CreateObject('InternetExplorer.Application'),
json = WSH.StdIn.ReadAll();
IE.Visible = 0;
IE.Navigate('about:blank');
while (IE.Busy || IE.ReadyState != 4) WSH.Sleep(25);
var JSON = IE.document.parentWindow.JSON,
pretty = JSON.stringify(JSON.parse(json), null, "\t");
WSH.Echo(pretty);
IE.Quit();
try { while (IE && IE.Busy) WSH.Sleep(25); }
catch(e) {}
Here's one more solution, this time using a batch / HTA hybrid. There's a <meta>
tag forcing the HTA interpreter into IE9 compatibility, thus including
support for JSON methods. This is faster than the IE method, but isn't completely invisible. The HTA window flashes on the screen for an instant, then closes itself.
<!-- : batch portion
@echo off & setlocal
rem // The for /f loop forces mshta to communicate with stdout
rem // as a console script host. Without for /f, attempting
rem // to write to stdout results in an invalid handle error.
for /f "delims=" %%I in ('mshta.exe "%~f0"') do echo(%%I
goto :EOF
end batch / begin HTA : -->
<meta http-equiv="x-ua-compatible" content="IE=9" />
<script>
var fso = new ActiveXObject('Scripting.FileSystemObject'),
stdin = fso.GetStandardStream(0),
stdout = fso.GetStandardStream(1),
json = stdin.ReadAll(),
pretty = JSON.stringify(JSON.parse(json), null, '\t');
close(stdout.Write(pretty));
</script>
The best solution I think is to use the scantily-documented htmlfile
COM object. Using the same trick with a <meta>
tag to force it into IE9 compatibility as was demonstrated with the HTA solution above, the htmlfile
COM object offers native support for JSON methods without requiring a library download, and without forking an additional windowed helper application. It just loads a dll.
@if (@CodeSection == @Batch) @then
@echo off & setlocal
cscript /nologo /e:JScript "%~f0"
goto :EOF
@end // end batch / begin JScript hybrid chimera
var htmlfile = WSH.CreateObject('htmlfile'),
json = WSH.StdIn.ReadAll();
htmlfile.write('<meta http-equiv="x-ua-compatible" content="IE=9" />');
var JSON = htmlfile.parentWindow.JSON,
pretty = JSON.stringify(JSON.parse(json), null, '\t');
htmlfile.close(WSH.Echo(pretty));
"filename.json" | python -m json.tool
but I am getting - No JSON object can be decoded. Can you please point me to the right syntax? – Quodlibetpython -m json.tool < filename.json
. If that doesn't work, you could trytype filename.json | python -m json.tool
. – Everybodypython -m json.tool < filename.json > beautified.json
– Everybodypython -m json.tool < filename.json > filename.json
but I'm not sure whether that'll successfully overwrite or give you a file-in-use error. If error, thenpython -m json.tool < filename.json > filename.tmp && move /y filename.tmp filename.json
. – Everybody