我有两个文件:changes.csv和sample.json。我的csv文件如下所示:
header "a", "b"
"a11","b1"
"a22","b2"
"a33","b3"
, json文件如下:
[
{"a":"a1","b":"b1"},
{"a":"a2","b":"b2"},
{"a":"a3","b":"b3"},
{"a":"a4","b":"b4"}
]
我需要写一个jq命令,它使用csv文件对json文件进行更改,即json文件的最终输出应该如下所示:
[
{"a":"a11","b":"b1"},
{"a":"a22","b":"b2"},
{"a":"a33","b":"b3"},
{"a":"a4","b":"b4"}
]
我写了下面的命令:
while IFS=",", read f1 f2
do
jq --argjson k1 $f1 --argjson k2 $f2 '(.[] | select(.b == $k2) | .a) |= $k1' sample.json| sponge sample.json
done < changes.csv
虽然,对于每次迭代,它都能够过滤和更新键"a"的值,但是当我试图将结果海绵到json文件中时,它无法这样做。我不知道我错过了什么。
假设CSV的行为相当良好,您可以这样写:
# Skip the CSV header row by NOT specifying the -n option
< changes.csv | jq -Rcr --argfile json sample.json '
def trim: sub("^[ t]*""; "") | sub(""[ t]*$";"");
INDEX(inputs | split(",") | map(trim) | select(length>0); .[1]) as $dict
| $json
| map( .a = $dict[.b][0] )
'
对于更混乱的CSV,您可能需要使用CSV-to- json或CSV-to- tsv工具(它们都可以很容易地用jq编写—例如参见https://rosettacode.org/wiki/Convert_CSV_records_to_TSV#jq)
如果你不喜欢使用——argfile选项,那么无论如何都可以使用其他方法读取这两个文件,例如,你可以使用——rawfile来读取CSV文件,留下STDIN来读取JSON文件。
对于纯jq
解决方案,您最好确保您的CSV在任何字段中不包含任何,
或"
或n
,否则代码将变得更加复杂。
因此,我用Miller提出了一个解决方案(可以在这里为几个操作系统提供),它可以轻松而健壮地完成任务:
mlr --icsv --ojson --no-jvstack join --ijson -f file.json -j 'b' --ul file.csv
[
{"b": "b1", "a": "a11"},
{"b": "b2", "a": "a22"},
{"b": "b3", "a": "a33"}
]
让我们分解命令:
mlr join -f file1 -j 'b' file2
将在字段
b
上加入file1和file2。当中存在b
以外的其他字段时,文件(例如a
),那么输出的是file2的值。
因此,为了使用CSV中的值更新JSON,file1应为JSON,file2为CSV。--ul
表示输出file1不可连接的行;如果没有它,输出将只包含已"配对"的记录。使用
join
动词,Miller允许指定不同的输入格式对于file1,小于file2;您将默认输入格式设置为--icsv
(用于处理file2),并在join
谓词之后使用--ijson
覆盖它(用于处理file1)。输出格式使用
--ojson
设置为JSON。--no-jvstack
表示输出一条"记录"。每行.
如果我将csv文件值转换为可读输入格式并使用——arg而不是——argjson,文件就会得到更新。因此,命令awk '{gsub(/"/,"")};1' b.csv > c.csv
将把csv文件转换为:
a11,b1
a22,b2
a33,b3
那么,现在如果应用命令:
while IFS=",", read f1 f2
do
jq --arg k1 $f1 --arg k2 $f2 '(.[] | select(.b == $k2) | .a) |= $k1' sample.json| sponge sample.json
done < c.csv
期望的变化将发生。
在旧格式中失败的合理原因是当我们使用"read"读取值时命令,将"a"
转换为""a""
。因此,每次我附加答案时,它都会返回空答案。
答案适用于任何类型的json格式。例如,如果json对象如下:
[
{
"d": { "a":"a1", "c":"c1"},
"b": "b1"
}
]
我们可以将命令更新为'(.[] | select(.b == $k2) | .d.a) |= $k1'
。
我没有尝试其他方法张贴,但我感谢你的帮助。谢谢大家(@peak &@Fravadona)