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).
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[
@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[
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With