jsonbatch-filecurlcode-formatting

beautify cURL download of JSON before writing to file


I am using cURL 7.39.0 to call a REST webservice and write the response to a .json file:

curl -L-X POST -b cookies.txt -H "Content-Type: application/json" http://localhost:7001/web/service/url -d {"param1":"value1"} -o "C:\output\serviceName.json" 

The response is written to the output file, but, without formatting:

{"status": "success","user": "name", "regId": "14420","subscriber": [{"memberFor":"3 years","lastLogin":"2 days ago"}]}

My questions are:

  1. Is there a way to format the JSON response before writing it to the output file (like below)?

    {
        "status": "success",
        "user": "name",
        "regId": "14420",
        "subscriber": [
            {
                "memberFor":"3 years",
                "lastLogin":"2 days ago"     
            }
        ]
    }
    
  2. If formatting is not possible via cURL, I wish to write a simple batch file to automatically open the JSON output file and apply formatting. Something like:

    @echo off
    cls
    "C:\Program Files\Notepad++\notepad++" "C:\output\serviceName.json" 
    pause
    

Are there any flags/options available in batch/console to achieve this?


Solution

  • 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));