使用 jq 在 JSON 字符串值中保留转义的正斜杠

使用 jq 在 JSON 字符串值中保留转义的正斜杠

根据 JSON 规范,正斜杠不用反斜杠转义但是他们是。
我有一个 JSON 文件,其中出于兼容原因转义了字符串值中的所有正斜杠(但不在键内):

{
  "proto://some/path": "\/\/some\/path"
}

但是,jq会自动删除这些反斜杠:

$ echo '{"proto://some/path":"\/\/some\/path"}' | jq -c .
{"proto://some/path":"//some/path"}

我需要输出是{"proto://some/path":"\/\/some\/path"}

我如何告诉jq不要更改任何字符串值并保留这些反斜杠?
或者,有没有办法重新添加这些反斜杠只针对价值观经历之后jq

答案1

如果你能的话我会感到惊讶。jq解码其输入,执行其操作并将结果对象编码为 json。编码输出时,输出的是JSON编码的字符串,s\前面的s信息/早已丢失。如果那些/最初写的地方也会发生同样的情况\u002f。您会发现出于同样的原因,它jq也会重新格式化1.011e2as 100INFas等。1.7976931348623157e+308

然而,JSON 是一种相对简单的文件格式,可以使用 perl 正则表达式等手动可靠地处理。

要在除对象键之外的所有字符串中的\每个字符串之前添加回s,您可以执行以下操作:/

jq... |
  perl -0777 -pe '
    s{"(?:\\.|.)*?"(\s*:)?}{
      $1 ? $& : $& =~ s{/}{\\/}gr
    }ge'

即使您有嵌入"s 和\s 的字符串(例如{"key": "//\"//\\"}),它也应该正常工作。

作为 的替代方案jq,您可以使用 JSON::PPperl模块,该模块可以被告知转义斜杠(尽管会出现在所有字符串中):

$ json_pp -json_opt escape_slash < your-file
{"proto:\/\/some\/path":"\/\/some\/path"}

如果您已经熟悉perl,那么学习曲线将不会比学习jq语法那么陡峭。

在任何情况下,虽然 JSON 格式允许/将 s 转义为\/(或\u002f类似任何字符)但不需要。根据我在网上读到的内容,这是允许的,因此可以通过编写HTML 标签来嵌入包含</在 HTML 标签中的 JSON 字符串。这就是为什么一些 JSON 编码器对它们进行编码,因为这使其更便携。但如果该 JSON 不打算像 HTML 那样嵌入,那么它可能并不重要。如果是的话,您可能希望在任何地方都使用这种编码,包括在对象键中。<script>"<\/whatever"\//

答案2

我假设某些不知道需要在 JSON 中编码字符串的进程已经插入了他们认为需要存储的文字字符串,但没有对其进行编码。由于反斜杠是不需要转义的转义字符,并且由于没有反斜杠本身转义文字反斜杠,因此当jq用于提取和解码字符串或出于其他原因处理文档时,它们似乎“消失”。

简而言之,向前斜杠不必转义(转义它们实际上是无操作),但如果您想将反斜杠保留为文字反斜杠,则需要转义反斜杠。

以下将递归地将文档中所有字符串值中的每个更改/\\/(这是您在 JSON 字符串中编写的方式)。\/请记住,当jq表达式处理数据时,jq解析器已经删除了反斜杠。

jq 'walk(if type == "string" then gsub("/"; "\\/") else . end)' file

对于给定的示例文档,这将生成

{
  "proto://some/path": "\\/\\/some\\/path"
}

从修改后的文档中提取和解码编码字符串值将为您提供\/\/some\/path

$ jq 'walk(if type == "string" then gsub("/"; "\\/") else . end)' file | jq -r '."proto://some/path"'
\/\/some\/path

如果您从头开始创建它,您将得到相同的 JSON 文档,如下所示:

$ jq -n --arg 'proto://some/path' '\/\/some\/path' '$ARGS.named'
{
  "proto://some/path": "\\/\\/some\\/path"
}

相关内容