在Batch中解析简单的JSON字符串

19

如何在批处理中解析简单的JSON字符串?

例如,如果有以下JSON字符串:

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

这个JSON字符串有许多元素,其中两个是“year”和“time”。我想从这个字符串中提取“year”和“time”的值,并将它们存储在两个变量中,而不使用任何外部库(即,我不想安装或下载任何用于此目的的外部工具,一个新安装的Windows应该具备所有必要的工具来执行此操作)。


5
出于病态的好奇心,为什么要使用批处理?JScript或PowerShell会更简单-它们都原生支持JSON。 - Mark Reed
2
好的,这个在PowerShell中怎么写呢?鉴于JSON文件,请在批处理脚本中返回年份和时间的值。 - Andrei
2
@MarkReed: 我想看到这样的“简单”(尽管速度较慢)PowerShell解决方案... - Aacini
2
你可以去掉@Aacini的引号。使用定制的字符串操作来解析JSON - 或者任何其他结构化格式 - 几乎总是与一个现有的、经过测试的库相比较会是错误的解决方案,特别是设计用来解析该格式的库。如果你使用这些语言中的任何一种,执行速度并不是你的优先考虑因素。 参见https://dev59.com/lWQn5IYBdhLWcg3wxZni。从一个JSON字符串中获取一个有用的PowerShell对象只需要一行代码:`$string | ConvertFrom-Json`。我认为这更简单。 - Mark Reed
1
@MarkReed 我强烈同意将JSON反序列化为对象比作为纯文本解析JSON要好得多。不幸的是,ConvertFrom-Json仅适用于PowerShell 3.0+。大多数Win 7框(无论我使用过哪个)默认为PowerShell 2.0,这使得使用该cmdlet部署脚本对许多最终用户具有问题。尽管如此,创建一个System.Web.Script.Serialization.JavaScriptSerializer对象只需要几行额外的代码 - rojo
显示剩余3条评论
5个回答

19
@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%"

输出:

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

这种方法的一个小不便是“时间”批处理动态变量将被新的JSON替换,但如果您使用setlocal命令,这一点就不会造成任何问题。

编辑:只是为了完成cz3ch的请求,将结果放入“字符串”数组而不是将它们放在单独的变量中:

@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[

好简单易懂的回答! - cascading-style
很好的解决方案!我们如何将它们放在一个数组中,而不是将它们放在单独的变量中,例如string [time]? - cz3ch
1
使用了这个解决方案,但稍微更新了一下,因为我的 JSON 没有空格:删除大括号 set "string=%string:~1,-1%",还从 set "string=%string::==%"set "%string:,=" & set "% 中删除了空格。这应该是一个答案,因为不需要额外的工具(如 poweshelljq)。 - oleksa

19

这是一个混合的批处理和PowerShell解决方案。 PowerShell将JSON文本对象化(反序列化)并以键=值格式输出,以便于批处理中的for /f循环捕获并设置为批处理变量。将此保存为.bat文件并尝试运行。

<# : 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] }

如果您只需要年份和时间值,只需使用%JSON[year]%%JSON[time]%

如果您从一个.json文件中读取JSON,您可以让PowerShell部分读取该文件,并将($env:JSON)替换为((gc jsonfile.json))。这样,您就不必依赖于JSON是多行和美化还是压缩。无论哪种方式,它都将被反序列化为相同的对象。


如果执行速度是一个问题,您可能更喜欢一个批处理+JScript混合解决方案。它将JSON反序列化为对象,与PowerShell解决方案相同,但是从批处理上下文调用JScript比从批处理调用PowerShell命令或脚本更快。

@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]);

与第一个 PowerShell 混合解决方案一样,如果你希望的话,可以通过在 JScript 部分从 .json 文件中读取来解析多行 JSON(通过创建 Scripting.FileSystemObject 对象并调用其 .OpenTextFile().ReadAll() 方法)。


这是另一个纯批处理方案,但它将 key=value 对设置为关联数组以避免像 Aacini 的解决方案一样覆盖 %time%。我真的认为最好将 JSON 解析为辅助语言中的对象,而不是在纯批处理中作为平面文本解析,但我也意识到最佳答案并不总是最受欢迎的。

@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[

5
这里有不同的方法,这是其中之一。
@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

这是第二个:

@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

这是硬编码的,例如如果我的JSON中有其他元素,并且我只想检索年份和时间,我如何知道它们的索引。 - Andrei
6
最佳方式是提出你想要答案的问题。 - foxidrive
6
那是一个相当哲学的评论,但我现在更新了我的问题。 - Andrei

3

使用https://stedolan.github.io/jq/,无需在批处理中使用powershell或字符串操作。

从JSON字符串获取字段值的示例:

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

echo %year%

给你2016年


2
这是最简单和最强大的解决方案。谢谢分享。 使用chocolatey非常简单: choco install jq - Igor Lino

1
使用jsonextractor.bat
for /f "tokens=* delims=" %%a in ('jsonextractor.bat simple.json year') do set "year=%%~a"

for /f "tokens=* delims=" %%a in ('jsonextractor.bat simple.json time') do set "time_=%%~a"

echo %year% -- %time_%

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接