2016-04-30 320 views
4

實際上,鍵必須在JSON對象內唯一(例如Does JSON syntax allow duplicate keys in an object?)。然而,假設我有以下內容的文件:JSON字段具有相同的名稱

{ 
    "a" : "1", 
    "b" : "2", 
    "a" : "3" 
} 

是否有轉換的重複鍵給數組的一個簡單的方法?因此,該文件就變成了:

{ 
    "a" : [ {"key": "1"}, {"key": "3"}], 
    "b" : "2" 
} 

或者類似,但它結合了重複鍵到一個數組(或查找和替代的方式來提取重複的鍵值)。

以下是一個Java的解決方案:Convert JSON object with duplicate keys to JSON array

有沒有辦法使用awk /慶典/蟒蛇做呢?

+0

json從哪裏來?在評估之前,您是否可以從服務器端或字符串訪問它?一旦json變成JS對象,我不認爲你可以做任何事情(也許我錯了) – Shovalt

+0

我生成json ...原則上我可以執行'jq -c'。',然後將它輸出爲單行字符串。 – econ

+1

[將具有重複鍵的JSON對象轉換爲JSON數組](http:// stackoverflow。com/questions/24416960/convert-json-object-with-duplicate-keys-to-json-array) – Shovalt

回答

5

如果輸入是真正與原語爲值的平JSON對象,這應該工作:

jq -s --stream 'group_by(.[0]) | map({"key": .[0][0][0], "value": map(.[1])}) | from_entries' 

{ 
    "a": [ 
    "1", 
    "3" 
    ], 
    "b": [ 
    "2" 
    ] 
} 

對於更復雜的輸出,這將需要真正理解如何--stream應該被使用,這是超越我。

+0

謝謝!這適用於我發佈的示例,並且幾乎適用於我的實際數據...此方法按關鍵字名稱(這非常合理)對結果進行分組,但在我的數據中,我有一些奇怪的情況,其中某些值可用於某些鍵,但不是其他人,順序很重要......我可能沒有正確寫入,但無論如何,您提供的代碼將幫助我解決實際問題(直到我設法在第一個實例中生成適當的JSON )。 – econ

+0

正如我在最初發表的評論中所說的那樣,生成簡單的JSON比在複雜的JSON上執行jq魔術更容易。 –

2

大廈使用-s --stream聖地亞哥的答案,下面的過濾器積聚的對象一步的時間,從而保持鍵和值的特定按鍵順序:

reduce (.[] | select(length==2)) as $kv ({}; 
     $kv[0][0] as $k 
     |$kv[1] as $v 
     | (.[$k]|type) as $t 
     | if $t == "null" then .[$k] = $v 
     elif $t == "array" then .[$k] += [$v] 
     else .[$k] = [ .[$k], $v ] 
     end) 

對於給定的輸入,結果是:

{ 
    "a": [ 
    "1", 
    "3" 
    ], 
    "b": "2" 
} 

爲了說明值的每個鍵的順序被保留,考慮以下輸入:

{ 
    "c" : "C", 
    "a" : "1", 
    "b" : "2", 
    "a" : "3", 
    "b" : "1" 
} 

通過過濾器上方產生的輸出是:

{ 
    "c": "C", 
    "a": [ 
    "1", 
    "3" 
    ], 
    "b": [ 
    "2", 
    "1" 
    ] 
} 
0

上峯的回答構建,下面的過濾器也作品多對象的輸入與嵌套對象而不slurp-選項-s)。

這不是一個答案,最初的問題,而是因爲這裏的JQ-FAQ鏈接,可能對於一些遊客

文件jqmergekeys.txt有用

def consumestream($arr): # Reads stream elements from stdin until we have enough elements to build one object and returns them as array 
input as $inp 
| if $inp|has(1) then consumestream($arr+[$inp]) # input=keyvalue pair => Add to array and consume more 
    elif ($inp[0]|has(1)) then consumestream($arr) # input=closing subkey => Skip and consume more 
    else $arr end; # input=closing root object => return array 

def convert2obj($stream): # Converts an object in stream notation into an object, and merges the values of duplicate keys into arrays 
reduce ($stream[]) as $kv ({}; # This function is based on http://stackoverflow.com/a/36974355/2606757 
     $kv[0] as $k 
     | $kv[1] as $v 
     | (getpath($k)|type) as $t # type of existing value under the given key 
     | if $t == "null" then setpath($k;$v) # value not existing => set value 
     elif $t == "array" then setpath($k; getpath($k) + [$v]) # value is already an array => add value to array 
     else setpath($k; [getpath($k), $v ]) # single value => put existing and new value into an array 
     end); 

def mainloop(f): (convert2obj(consumestream([input]))|f),mainloop(f); # Consumes streams forever, converts them into an object and applies the user provided filter 
def mergeduplicates(f): try mainloop(f) catch if .=="break" then empty else error end; # Catches the "break" thrown by jq if there's no more input 

#---------------- User code below --------------------------  

mergeduplicates(.) # merge duplicate keys in input, without any additional filters 

#mergeduplicates(select(.layers)|.layers.frame) # merge duplicate keys in input and apply some filter afterwards 

例子:

tshark -T ek | jq -nc --stream -f ./jqmergekeys.txt