【问题标题】:jq - bash script for csv to json with arrays?jq - 用于 csv 到 json 数组的 ba​​sh 脚本?
【发布时间】:2017-08-15 17:41:25
【问题描述】:

我一直在使用 jq 从 curl 响应中解析 JSON,这非常酷。

我现在要做的是获取我在 CSV 中拼凑在一起的各种信息,并将其转换为 JSON,以便我可以将其转换为 API。这是我尝试以编程方式复制的示例 JSON:

{
  "title": "New Story",
  "channels": [
    {
      "id": "65tyc2TLUZsO"
    }
  ],
  "description": "Story Description Here",
  "new_files": [
    {
      "filename": "419155345a7b449df3baca76694b64efbec9bcf3983b51e02f92e7ef29fc26ee.pptx",
      "description": "File Description ABC"
    },
    {
    "filename": "5cdd90989c03d3fb619df6f9294b1fcb537b4f3b55737465930b220507f30e75.pdf",
    "description":"File Description XYZ"
    }
  ]
}

标题、id、描述、文件名和(文件)描述值都在 CSV 中。我想不通的是如何从我所拥有的中生成 JSON。

鉴于以下情况,我不知道应该如何格式化我的 CSV 文件:
1. 我在顶层有一些键:值对(标题、描述),然后还有一些带有键:值对的数组。我可以告诉你,“通道”数组中总是有 1 个值。
2. 我不会在 new_files 数组中拥有相同数量的文件,因此能够动态构建该数组会很棒。

如果有人有关于这种事情的教程可以指点我,那就太棒了。我敢肯定我不是第一个尝试这个的人。我正在为这些东西使用 bash 脚本(据我所知),但我并不反对其他解决方案。 (我只是需要更长的时间来学习。)

在高层次上,我了解我想要做什么以及从哪里提取信息,我应该在哪里拥有哪些变量等等,我只是在实施细节方面遇到了一些问题。

【问题讨论】:

  • 首先,请放心,jq 对于您所描述的任务来说是一个很棒的工具。其次,您表示您在格式化 CSV 文件方面具有一定的灵活性,这使得您很难确定您遇到的问题。我建议您描述一种您想到的特定格式,以便我们有一些开始。第三,您可能需要咨询 jq github.com/stedolan/jq/wiki/FAQgithub.com/stedolan/jq/wiki/Cookbook 以获取 CSV 到 JSON 的示例。
  • 这是一个 csv 格式的示例:标题、频道、描述、文件名、文件描述新故事、65tyc2TLUZsO、这里的故事描述、419155345a7b449df3baca76694b64efbec9bcf3983b51e02f92e7ef29fc26ee.pptx、文件描述 ABC 新故事、 5cdd90989c03d3fb619df6f9294b1fcb537b4f3b55737465930b220507f30e75.pdf,文件说明 XYZ 这里我每个文件有 1 行。但是,我如何区分这些文件进入一个数组,我只有 1 个“标题”值,通道是一个数组,但只有 1 个 key:value。

标签: json csv jq


【解决方案1】:

也许这会有所帮助。首先编写一个函数以将输出的嵌套表示转换为平面表示:

def flatten:
    {
      title: .title,
      id:    .channels[].id,
      story: .description,
    } + .new_files[]
;

这会将您的示例 json 转换为对象流:

{
  "title": "New Story",
  "id": "65tyc2TLUZsO",
  "story": "Story Description Here",
  "filename": "419155345a7b449df3baca76694b64efbec9bcf3983b51e02f92e7ef29fc26ee.pptx",
  "description": "File Description ABC"
}
{
  "title": "New Story",
  "id": "65tyc2TLUZsO",
  "story": "Story Description Here",
  "filename": "5cdd90989c03d3fb619df6f9294b1fcb537b4f3b55737465930b220507f30e75.pdf",
  "description": "File Description XYZ"
}

可以很容易地转换成csv之类的

"New Story","65tyc2TLUZsO","Story Description Here","419155345a7b449df3baca76694b64efbec9bcf3983b51e02f92e7ef29fc26ee.pptx","File Description ABC"
"New Story","65tyc2TLUZsO","Story Description Here","5cdd90989c03d3fb619df6f9294b1fcb537b4f3b55737465930b220507f30e75.pdf","File Description XYZ"

使用如下过滤器:

  flatten
| [.title, .id, .story, .filename, .description ]
| @csv

要从这个 csv 表示转到对象流,您可以使用 jq -s-R 选项以及类似的函数

def readcsv:
      split("\n")
    | .[]
    | select(length > 0)  
    | split(",")
    | map(fromjson)
    | {
          title:       .[0]
        , id:          .[1]
        , story:       .[2]
        , filename:    .[3]
        , description: .[4]
      }
;

并将该对象流重新组合到您的原始 json 中,您可以使用类似的函数

def unflatten:
      group_by(.title)
    | .[]
    | {
          title:       .[0].title
        , description: .[0].story
      }
      + { channels:    map(.id) | unique | map({id:.}) }
      + { new_files:   map({filename, description}) | unique }
;

通过组合过滤器

  [ readcsv ]
| unflatten

这些函数对可能需要审查的数据字段之间的关系做出了一些假设。特别是,您可能不想像我那样完全非规范化 id 和 filename/description 列。但是,一旦您拥有像这样的工具来从嵌套 json 到平面 csv 来回转换,您就可以对每种表示进行试验,直到您满意为止。

【讨论】:

    【解决方案2】:

    为了清楚起见,这是一个解决方案,涉及两次 jq 调用:

     jq -R 'split(",")' input.csv | jq -f program.jq
    

    (这两个调用可以很容易地合并为一个,如下所述。)

    第一次调用会发出一个数组流。第一个数组对应于标题,为了清楚起见,将简单地丢弃它。在此处使用inputs 不带 -n 命令行选项完成丢弃。

    这里是program.jq:

    # Input: an array of "filename, description" pairs
    # Output: an array of {filename: _, description: _} objects
    def new_files:
      if length == 0 then []
      else [{filename: .[0], description: .[1]}] + (.[2:] | new_files)
      end;
       
    inputs
    | {title: .[0], channels: [{id: .[1]}], description: .[2] } 
    + {new_files: (.[3:] | new_files) }
    

    如果您的 jq 没有inputs,那么您将使用-s 命令行选项并将上面的inputs 替换为.[1:][]

    输出

    根据您的输入,两种情况下的输出(使用inputs.[1:][])将是:

     {
      "title": "New Story",
      "channels": [
        {
          "id": "65tyc2TLUZsO"
        }
      ],
      "description": "Story Description Here",
      "new_files": [
        {
          "filename": "419155345a7b449df3baca76694b64efbec9bcf3983b51e02f92e7e‌​f29fc26ee.pptx",
          "description": "File Description ABC New Story"
        },
        {
          "filename": "65tyc2TLUZsO",
          "description": "Story Description Here"
        },
        {
          "filename": "5cdd90989c03d3fb619df6f9294b1fcb537b4f3b55737465930b220‌​507f30e75.pdf",
          "description": "File Description XYZ "
        }
      ]
    }
    

    单次调用

    避免两次调用 jq 的一种方法是使用 -R 和 -s 选项调用 jq,使用上述 new_files 定义和以下程序:

    split("\n")
    | map(split(","))
    | .[1:][]
    | {title: .[0], channels: [{id: .[1]}], description: .[2] } 
    + {new_files: (.[3:] | new_files) }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-10
      • 1970-01-01
      • 2017-05-27
      • 2010-12-25
      • 2018-05-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多