วิธีผสานออบเจ็กต์ JSON 2 รายการจาก 2 ไฟล์โดยใช้ jq


125

ฉันใช้jq tools (jq-json-processor) ในเชลล์สคริปต์เพื่อแยกวิเคราะห์ json

ฉันมีไฟล์ json 2 ไฟล์และต้องการรวมเป็นไฟล์เดียว

นี่คือเนื้อหาของไฟล์:

file1

{
    "value1": 200,
    "timestamp": 1382461861,
    "value": {
        "aaa": {
            "value1": "v1",
            "value2": "v2"
        },
        "bbb": {
            "value1": "v1",
            "value2": "v2"
        },
        "ccc": {
            "value1": "v1",
            "value2": "v2"
        }
    }
}

file2

{
    "status": 200,
    "timestamp": 1382461861,
    "value": {
        "aaa": {
            "value3": "v3",
            "value4": 4
        },
        "bbb": {
            "value3": "v3"
        },      
        "ddd": {
            "value3": "v3",
            "value4": 4
        }
    }
}

ผลลัพธ์ที่คาดหวัง

{
    "value": {
        "aaa": {
            "value1": "v1",
            "value2": "v2",
            "value3": "v3",
            "value4": 4
        },
        "bbb": {
            "value1": "v1",
            "value2": "v2",
            "value3": "v3"
        },
        "ccc": {
            "value1": "v1",
            "value2": "v2"
        },
        "ddd": {
            "value3": "v3",
            "value4": 4
        }
    }
}

ฉันลองใช้ Combinaison มากมาย แต่ผลลัพธ์เดียวที่ฉันได้รับคือสิ่งต่อไปนี้ซึ่งไม่ใช่ผลลัพธ์ที่คาดหวัง:

{
  "ccc": {
    "value2": "v2",
    "value1": "v1"
  },
  "bbb": {
    "value2": "v2",
    "value1": "v1"
  },
  "aaa": {
    "value2": "v2",
    "value1": "v1"
  }
}
{
  "ddd": {
    "value4": 4,
    "value3": "v3"
  },
  "bbb": {
    "value3": "v3"
  },
  "aaa": {
    "value4": 4,
    "value3": "v3"
  }
}

ใช้คำสั่งนี้:

jq -s '.[].value' file1 file2

1
คุณลอง jsontool หรือยัง? github.com/trentm/json
Nano Taboada

ยังไม่ได้ฉันจะดู Thx
Janfy

ทำได้โดยjsonใช้:cat f1 f2 | json --deep-merge
xer0x

คุณได้รับjson@ xer0x ที่ไหน / อย่างไร
กัส

@Gus โอ้เพื่อรับjsonเครื่องมือไปที่github.com/trentm/json
xer0x

คำตอบ:


158

ตั้งแต่ 1.4 สิ่งนี้สามารถทำได้ด้วยตัว*ดำเนินการ เมื่อได้รับวัตถุสองชิ้นมันจะรวมเข้าด้วยกันซ้ำ ๆ ตัวอย่างเช่น,

jq -s '.[0] * .[1]' file1 file2

สำคัญ: สังเกต-s (--slurp)แฟล็กซึ่งทำให้ไฟล์อยู่ในอาร์เรย์เดียวกัน

จะได้รับคุณ:

{
  "value1": 200,
  "timestamp": 1382461861,
  "value": {
    "aaa": {
      "value1": "v1",
      "value2": "v2",
      "value3": "v3",
      "value4": 4
    },
    "bbb": {
      "value1": "v1",
      "value2": "v2",
      "value3": "v3"
    },
    "ccc": {
      "value1": "v1",
      "value2": "v2"
    },
    "ddd": {
      "value3": "v3",
      "value4": 4
    }
  },
  "status": 200
}

หากคุณต้องการกำจัดคีย์อื่น ๆ ด้วย (เช่นผลลัพธ์ที่คุณคาดหวัง) วิธีหนึ่งที่จะทำได้คือ:

jq -s '.[0] * .[1] | {value: .value}' file1 file2

หรือค่อนข้างมีประสิทธิภาพมากกว่า (เพราะไม่รวมค่าอื่น ๆ ):

jq -s '.[0].value * .[1].value | {value: .}' file1 file2

2
หมายเหตุ : -sแฟล็กมีความสำคัญเนื่องจากไม่มีวัตถุทั้งสองไม่อยู่ในอาร์เรย์
John Morales

1
@SimoKinnunen ที่นี่เรากำลังรวมไฟล์ json สองไฟล์ เป็นไปได้ไหมที่จะมีตัวแปร json 1 ตัวและไฟล์ json อื่น ๆ ฉันลองแล้ว แต่ดูเหมือนว่าจะไม่ได้ผลสำหรับฉัน!
Jayesh Dhandha

หากไฟล์แต่ละไฟล์ถูกจัดเรียงตามคีย์เป็นไปได้ไหมที่จะรักษาลำดับในไฟล์ผลลัพธ์
ลีโอ

@SimoKinnunen ฉันใช้คำสั่งนี้: "jq -s 'add' config.json other / *. json" แต่เมื่อฉันทำ "cat config.json" มันจะไม่ถูกรวมเข้าด้วยกัน ฉันจะใส่เอาต์พุตกลับไปที่ไฟล์ได้อย่างไร
Renato Bibiano

63

ใช้jq -s add:

$ echo '{"a":"foo","b":"bar"} {"c":"baz","a":0}' | jq -s add
{
  "a": 0,
  "b": "bar",
  "c": "baz"
}

สิ่งนี้อ่านข้อความ JSON ทั้งหมดจาก stdin เป็นอาร์เรย์ (jq -sทำอย่างนั้น) แล้ว "ลด"

( addหมายถึงdef add: reduce .[] as $x (null; . + $x);ซึ่งจะวนซ้ำค่าของอาร์เรย์อินพุต / อ็อบเจ็กต์และเพิ่มเข้าไปการเพิ่มอ็อบเจ็กต์ == ผสาน)


4
เป็นไปได้ไหมที่จะทำการผสานแบบวนซ้ำ (ตัวดำเนินการ *) ด้วยวิธีนี้
Dave Foster

1
@DaveFoster ใช่มีบางอย่างเช่นreduce .[] as $x ({}; . * $x)- ดูคำตอบของ jribด้วย
Chriki

สิ่งนี้ช่วยให้ฉันรวมไฟล์ json สองไฟล์โดยใช้ Ansible และ jq ขอบคุณ
Felipe Alvarez

35

ใครจะรู้ว่าคุณยังต้องการ แต่นี่คือทางออก

เมื่อคุณไปถึง--slurpตัวเลือกแล้วมันง่ายมาก!

--slurp/-s:
    Instead of running the filter for each JSON object in the input,
    read the entire input stream into a large array and run the filter just once.

จากนั้น+โอเปอเรเตอร์จะทำสิ่งที่คุณต้องการ:

jq -s '.[0] + .[1]' config.json config-user.json

(หมายเหตุ: หากคุณต้องการรวมวัตถุภายในแทนที่จะเขียนทับไฟล์ด้านซ้ายด้วยไฟล์ที่ถูกต้องคุณจะต้องดำเนินการด้วยตนเอง)


19

นี่คือเวอร์ชันที่ทำงานแบบวนซ้ำ (โดยใช้*) กับวัตถุจำนวนหนึ่งโดยพลการ:

echo '{"A": {"a": 1}}' '{"A": {"b": 2}}' '{"B": 3}' | jq --slurp 'reduce .[] as $item ({}; . * $item)'
{
  "A": {
    "a": 1,
    "b": 2
  },
  "B": 3
}

2
คำตอบที่ดี ทำงานได้ดีเมื่อรวมไฟล์ทั้งหมดในไดเรกทอรี:jq -s 'reduce .[] as $item ({}; . * $item)' *.json
John Ellmore

7

ขั้นแรก {"value": .value} สามารถย่อให้เหลือเพียง {value}

ประการที่สองตัวเลือก --argfile (มีใน jq 1.4 และ jq 1.5) อาจเป็นที่สนใจเนื่องจากหลีกเลี่ยงการใช้ตัวเลือก --slurp

เมื่อรวมสิ่งเหล่านี้เข้าด้วยกันวัตถุทั้งสองในสองไฟล์สามารถรวมกันได้ตามวิธีที่กำหนดดังนี้:

$ jq -n --argfile o1 file1 --argfile o2 file2 '$o1 * $o2 | {value}'

แฟล็ก '-n' บอกให้ jq ไม่อ่านจาก stdin เนื่องจากอินพุตมาจากอ็อพชัน --argfile ที่นี่


2
jq เลิกใช้งาน--argileสำหรับ--slurpfile
David Groomes

3

สามารถใช้เพื่อรวมไฟล์จำนวนเท่าใดก็ได้ที่ระบุในคำสั่ง:

jq -rs 'reduce .[] as $item ({}; . * $item)' file1.json file2.json file3.json ... file10.json

หรือสิ่งนี้สำหรับไฟล์จำนวนเท่าใดก็ได้

jq -rs 'reduce .[] as $item ({}; . * $item)' ./*.json

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.