Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parse simple JSON string in Batch

Tags:

batch-file

How do you parse a simple JSON string in Batch?

For example, if I have the following JSON string:

{ "...":"...", "year": 2016, "time": "05:01", "...":"...", }

This JSON string has a number of elements and two of them are "year" and "time". From this string, I want to extract the values for "year" and "time" in two variables without using any external libraries (i.e., I don't want to install download any external utils for this, a freshly installed Windows should have all the necessary tools to do this).

like image 973
Andrei Avatar asked Apr 02 '16 14:04

Andrei


3 Answers

Here's a hybrid Batch + PowerShell solution. The PowerShell objectifies (deserializes) the JSON text and outputs it in key=value format to be captured and set as batch variables by the batch for /f loop. Save this with a .bat extension and give it a shot.

<# : batch portion (contained within a PowerShell multi-line comment)
@echo off & setlocal

set "JSON={ "year": 2016, "time": "05:01" }"

rem # re-eval self with PowerShell and capture results
for /f "delims=" %%I in ('powershell "iex (${%~f0} | out-string)"') do set "%%~I"

rem # output captured results
set JSON[

rem # end main runtime
goto :EOF

: end batch / begin PowerShell hybrid code #>

add-type -AssemblyName System.Web.Extensions
$JSON = new-object Web.Script.Serialization.JavaScriptSerializer
$obj = $JSON.DeserializeObject($env:JSON)

# output object in key=value format to be captured by Batch "for /f" loop
foreach ($key in $obj.keys) { "JSON[{0}]={1}" -f $key, $obj[$key] }

Then if you want only the year and time values, just use %JSON[year]% or %JSON[time]%.

If you're reading your JSON from a .json file, you could have the PowerShell portion read the file, replacing ($env:JSON) with ((gc jsonfile.json)). Then you wouldn't be dependent at all on whether your JSON is multi-line and beautified or minified. It'll be deserialized all the same either way.


If execution speed is a concern, you might prefer a Batch + JScript hybrid solution. It deserializes the JSON into an object the same as the PowerShell solution, but invoking JScript from a Batch context is faster than invoking a PowerShell command or script from Batch.

@if (@CodeSection == @Batch) @then
@echo off & setlocal

set "JSON={ "year": 2016, "time": "05:01" }"

rem // re-eval self with JScript interpreter and capture results
for /f "delims=" %%I in ('cscript /nologo /e:JScript "%~f0"') do set "%%~I"

rem // output captured results
set JSON[

rem // end main runtime
goto :EOF

@end // end Batch / begin JScript hybrid code

var htmlfile = WSH.CreateObject('htmlfile'),
    txt = WSH.CreateObject('Wscript.Shell').Environment('process').Item('JSON');

htmlfile.write('<meta http-equiv="x-ua-compatible" content="IE=9" />');
var obj = htmlfile.parentWindow.JSON.parse(txt);
htmlfile.close();

for (var i in obj) WSH.Echo('JSON[' + i + ']=' + obj[i]);

And as is the case with the first PowerShell hybrid solution, you can parse multi-line JSON by reading the .json file from within the JScript portion if you wish (by creating a Scripting.FileSystemObject object and calling its .OpenTextFile() and .ReadAll() methods).


Here's another pure batch solution, but one which sets key=value pairs as an associative array to avoid stomping on %time% as Aacini's solution does. I really think it's better to parse JSON as an object in a helper language rather than as flat text in pure batch, but I also realize that the best answer is not always the most popular.

@echo off
setlocal

set "JSON={ "other": 1234, "year": 2016, "value": "str", "time": "05:01" }"

set "JSON=%JSON:~1,-1%"
set "JSON=%JSON:":=",%"

set mod=0
for %%I in (%JSON%) do (
    set /a mod = !mod
    setlocal enabledelayedexpansion
    if !mod! equ 0 (
        for %%# in ("!var!") do endlocal & set "JSON[%%~#]=%%~I"
    ) else (
        endlocal & set "var=%%~I"
    )
)

set JSON[
like image 178
rojo Avatar answered Oct 21 '22 05:10

rojo


@echo off
setlocal

set string={ "other": 1234, "year": 2016, "value": "str", "time": "05:01" }

rem Remove quotes
set string=%string:"=%
rem Remove braces
set "string=%string:~2,-2%"
rem Change colon+space by equal-sign
set "string=%string:: ==%"
rem Separate parts at comma into individual assignments
set "%string:, =" & set "%"

echo other="%other%"
echo year="%year%"
echo value="%value%"
echo time="%time%"

Output:

other="1234"
year="2016"
value="str"
time="05:01"

A small inconvenient of this method is that "time" Batch dynamic variable will be replaced by the new JSON one, but if you use setlocal command, this point don't cause any problem.

EDIT: Just to complete cz3ch's request of put results into "string" array instead of placing them inside individual variables:

@echo off
setlocal

set string={ "other": 1234, "year": 2016, "value": "str", "time": "05:01" }

rem Remove quotes
set string=%string:"=%
rem Remove braces
set "string=%string:~2,-2%"
rem Change colon+space by "]equal-sign"
set "string=%string:: =]=%"
rem Separate parts at comma into individual array assignments
set "string[%string:, =" & set "string[%"

set string[
like image 23
Aacini Avatar answered Oct 21 '22 04:10

Aacini


There are different ways, here's one.

@echo off
set string={ "year": 2016, "time": "05:01" }
set string=%string:"=%

for /f "tokens=3,5" %%a in ('echo %string%') do set d=%%a&set t=%%b
echo -%d%- -%t%-
pause & goto :EOF

and here's a second:

@echo off
set string={ "year": 2016, "time": "05:01" }

for /f "tokens=3,5" %%a in ('echo %string%') do set d=%%~a&set t=%%~b
echo -%d%- -%t%-
pause & goto :EOF
like image 21
foxidrive Avatar answered Oct 21 '22 04:10

foxidrive