使用jq与xmlstarlet转JSON为XML失败及JUnit格式需求问题
问题场景
尝试用以下jq+xmlstarlet命令将OWASP ZAP生成的JSON(含站点及告警信息)转成XML:
jq -r 'def walk(f): . as $in | if type == "object" then reduce keys_unsorted[] as $key (.; .[$key] = ($in[$key] | walk(f))) | f else f end; walk(tostring) | gsub("&"; "&")' input.json | xmlstarlet fo --omit-decl --indent-tab > output.xml
执行后报错:
-:1.1: Start tag expected, '<' not found {"@programName":"OWASP ZAP","@version":"D-2023-05-30","@generated":"Wed, 31 May ^
已确认JSON有效,且实际需要转换为指定JUnit格式XML,之前用json_to_xml.jq生成的XML不符合需求。
错误原因
原命令的核心问题是:jq输出的仍是JSON格式字符串(仅将所有值转为字符串),并非合法XML结构。而xmlstarlet fo是用于格式化已有XML的工具,无法将JSON直接转换为XML,因此会报“缺少起始标签”的错误。
解决方案:用jq直接生成JUnit格式XML
既然目标是生成指定的JUnit XML,直接用jq构建对应XML标签结构即可,无需依赖xmlstarlet做格式转换(jq可直接输出格式化后的XML)。
示例jq脚本(适配OWASP ZAP JSON结构)
假设你的OWASP ZAP JSON包含site和alerts核心节点,以下脚本会将告警映射为JUnit的<testcase>,并将中高危告警标记为失败:
def escape_xml: gsub("&"; "&") | gsub("<"; "<") | gsub(">"; ">") | gsub("\""; """) | gsub("'"; "'"); "<testsuites>" + (.site[] | "<testsuite name=\"\(.name | escape_xml)\" tests=\"\(.alerts | length)\" failures=\"\(.alerts[] | select(.riskcode >= 2) | length)\">" + (.alerts[] | "<testcase name=\"\(.alert | escape_xml)\" classname=\"\(.pluginId | escape_xml)\">" + (if .riskcode >= 2 then "<failure message=\"\(.riskdesc | escape_xml)\">\(.description | escape_xml)\n\(.solution | escape_xml)</failure>" else "" end) + "</testcase>" ) + "</testsuite>" ) + "</testsuites>"
使用方式
将上述脚本保存为zap_to_junit.jq,执行命令:
jq -rf zap_to_junit.jq input.json > output.xml
自定义调整
如果你的JUnit格式有特殊要求,可直接修改jq脚本:
- 调整
<testsuite>的属性,添加timestamp、skipped等字段 - 修改
<testcase>结构,比如增加time属性(若ZAP提供耗时数据) - 调整风险等级判断逻辑,例如将低危告警也标记为失败
内容的提问来源于stack exchange,提问作者Krishan Gopal
相关产品推荐
相关产品推荐

