2015-10-22 33 views
4

我有以下的JSON行例如:如何使用JQ將對象列表展開爲非規格化對象?

{"toplevel_key": "top value 1", "list": [{"key1": "value 1", "key2": "value 2"},{"key1": "value 3", "key2": "value 4"}]} 
{"toplevel_key": "top value 2", "list": [{"key1": "value 5", "key2": "value 6"}]} 

我想用它JQ,展開列表以固定數量的「列」,以平JSON對象列表結束了,轉換格式如下:

{ 
    "top-level-key": "top value 1", 
    "list_0_key1": "value 1", 
    "list_0_key2": "value 2", 
    "list_1_key1": "value 3", 
    "list_1_key2": "value 4", 
} 
{ 
    "top-level-key": "top value 2", 
    "list_0_key1": "value 4", 
    "list_0_key2": "value 5", 
    "list_1_key1": "", 
    "list_1_key2": "", 
} 

注:其實我希望他們每行一個,這裏格式化爲可讀性。

唯一的辦法我能得到我想要的輸出是通過我的JQ表達寫出的所有列:

$ cat example.jsonl | jq -c '{toplevel_key, list_0_key1: .list[0].key1, list_0_key2: .list[0].key2, list_1_key1: .list[1].key1, list_1_key2: .list[1].key2}' 

這讓我我想要的結果,但我必須寫手動所有固定的「列」(並且在生產中它將比這更多)。

我知道我可以使用腳本來產生JQ代碼,但我不感興趣在那樣的解決方案 - 它不會解決我的問題,因爲這是隻接受JQ應用。

有沒有辦法在純JQ中做到這一點?

這是我能得到迄今:

$ cat example.jsonl | jq -c '(.list | to_entries | map({("list_" + (.key | tostring)): .value})) | add' 
{"list_0":{"key1":"value 1","key2":"value 2"},"list_1":{"key1":"value 3","key2":"value 4"}} 
{"list_0":{"key1":"value 5","key2":"value 6"}} 

回答

8

只要你知道特定鍵的名稱,傑夫的答案是偉大的。下面是一個不硬編碼特定的鍵名的答案,那就是,它適用於任何嵌套結構,對象和層次:

[leaf_paths as $path | { 
    "key": $path | map(tostring) | join("_"), 
    "value": getpath($path) 
}] | from_entries 

的解釋:paths是一個內置函數,其輸出表示位置的數組您傳遞給它的每個元素的元素遞歸地:所述數組中的元素是導致請求的數組元素的有序鍵名稱和索引。 leaf_paths是它的一個版本,它只獲取「葉」元素的路徑,即不包含其他元素的元素。

爲了澄清,給定了輸入[[1, 2]]paths將輸出[0], [0, 0], [0, 1](即,路徑[1, 2],分別12,),同時leaf_paths只會輸出[0, 0], [0, 1]

這是最難的部分。之後,我們得到$path(形式爲["list", 1, "key2"])的每個路徑將其每個元素轉換爲其帶有下劃線的map(tostring)(它給了我們["list", "1", "key2"])和join的字符串表示。我們把它作爲我們想要創建的對象中的「條目」的關鍵:作爲值,我們得到在給定的$path處的原始對象的值。

最後,我們使用from_entries將一組鍵值對轉換爲JSON對象。這會給我們一個類似於傑夫答案的輸出:也就是說,只有帶有值的鍵出現。

出現在任何輸入對象。但是,你原來的問題請求的值出現在所有的輸出,與輸入丟失時,設置爲空字符串對應的值。下面是做這個JQ程序:傑夫在他回答說,你需要發出聲音(-s)所有輸入值,它是可能的:

(map(leaf_paths) | unique) as $paths | 
map([$paths[] as $path | { 
    "key": $path | map(tostring) | join("_"), 
    "value": (getpath($path) // "") 
}] | from_entries)[] 

你會發現,這是非常類似於第一程序:主要區別在於我們獲得了對齊對象中的所有唯一路徑,如$paths,並且對於每個對象,我們都會通過這些路徑而不是通過該對象的路徑。我們還使用替代運算符(//)將缺失值設置爲空字符串。

希望這會有所幫助!

+0

哇,夥計,你剛把我弄走了! :) 我偶然發現了這個路徑函數,並認爲它可以幫助,但看不到如何適合這些塊。 很高興知道那個'//'操作符,有用的東西! 謝謝! – elias

+2

那些路徑......只是習慣使用它們而已。好的。我必須將它們用於我的曲目。 –

4

這裏是你如何構建起來:

{ "top-level-key": .toplevel_key } + ([ 
    range(.list|length) as $i 
     | .list[$i] 
     | to_entries[] 
     | .key = "list_\($i)_\(.key)" 
    ] | from_entries) 

這將地圖中的對應列表條目。

{ 
    "top-level-key": "top value 1", 
    "list_0_key1": "value 1", 
    "list_0_key2": "value 2", 
    "list_1_key1": "value 3", 
    "list_1_key2": "value 4" 
} 
{ 
    "top-level-key": "top value 2", 
    "list_0_key1": "value 5", 
    "list_0_key2": "value 6" 
} 

如果您需要填充它,您將不得不沉浸在結果中以確定實際需要多少並添加填充。但現在我暫且放棄。

+0

謝謝,這真棒! :) – elias

相關問題