วิธีดึงข้อมูลจากไฟล์ JSON


13

ฉันมีช่องค้นหาคำตอบสำหรับคำถามของฉัน แต่ไม่พบหรือดีกว่าบอกว่าฉันไม่ได้รับสิ่งที่ฉันพบ ดังนั้นให้พูดคุยเกี่ยวกับสิ่งที่เป็นปัญหาของฉัน ฉันกำลังใช้ซอฟต์แวร์ควบคุมสมาร์ทโฮมกับ Raspberry Pi และเมื่อฉันพบว่าเมื่อสุดสัปดาห์นี้ด้วยการรับ pilight ฉันสามารถจับข้อมูลจากเซ็นเซอร์วัดอุณหภูมิภายนอกของฉันได้ ผลลัพธ์ของการได้รับ pilight ดูเหมือนว่า:

{
        "message": {
                "id": 4095,
                "temperature": 409.5
        },
        "origin": "receiver",
        "protocol": "alecto_wsd17",
        "uuid": "0000-b8-27-eb-0f3db7",
        "repeats": 3
}
{
        "message": {
                "id": 1490,
                "temperature": 25.1,
                "humidity": 40.0,
                "battery": 1
        },
        "origin": "receiver",
        "protocol": "alecto_ws1700",
        "uuid": "0000-b8-27-eb-0f3db7",
        "repeats": 3
}
{
        "message": {
                "id": 2039,
                "temperature": 409.5
        },
        "origin": "receiver",
        "protocol": "alecto_wsd17",
        "uuid": "0000-b8-27-eb-0f3db7",
        "repeats": 4
}

ตอนนี้คำถามของฉันกับคุณ: วิธี heck ฉันสามารถแยกอุณหภูมิและความชื้นจากที่ id คือ 1490 และคุณจะแนะนำให้ฉันตรวจสอบนี้บ่อย? โดยงาน cron ที่รันทุก 10 นาทีสร้างเอาต์พุตของ pilight-accept, แยกข้อมูลของเอาต์พุตและส่งไปยัง Smart Home Control Api

ใครบางคนมีความคิด - ขอบคุณมาก


3
รูปแบบที่น่าจะเป็นJSON มีหลายวิธีในการแยกวิเคราะห์ JSON ขึ้นอยู่กับสิ่งที่คุณพอใจ งูใหญ่? JavaScript? อื่น ๆ อีก?
muru

ฉันรู้ Python และ JavaScript เป็นส่วนใหญ่ฉันรู้ว่า C ++ และ C # แต่หลังจากที่ได้เห็นทุก awk และคำสั่ง sed ผมว่าต้องมีบาง xD คำสั่งง่าย
ราอูลการ์เซียซานเชซ

1
ไม่ใช่เรื่องยากawkและsedให้เอาต์พุต JSON ยังคงรูปแบบที่แสดงที่นี่ซึ่งไม่จำเป็น - ช่องว่างไม่สำคัญสำหรับ JSON ตัวอย่างเช่นawkคำสั่งนี้: awk '/temperature|humidity/ {print $2}'ปิด
muru

4
ด้วยการksh93แยก json ถูกสร้างreadขึ้นเพื่อ
mikeserv

1
ตรวจสอบ wheezy-backport มันอาจจะอยู่ในนั้นช่วยให้คุณอัพเกรดเป็นเจสซี (ยกเว้นว่าคุณวางแผนที่จะอัพเกรดอยู่ดี) AHA! มันกลับไปที่เสียงฮืด ๆ packages.debian.org/wheezy-backports/jq
cas

คำตอบ:


23

คุณสามารถใช้jqเพื่อประมวลผลไฟล์ json ในเชลล์

ตัวอย่างเช่นฉันบันทึกไฟล์ json ตัวอย่างของคุณเป็นraul.jsonแล้ววิ่ง:

$ jq .message.temperature raul.json 
409.5
25.1
409.5
$ jq .message.humidity raul.json 
null
40
null

jqพร้อมใช้งานก่อนบรรจุสำหรับ linux distros ส่วนใหญ่

อาจมีวิธีที่จะทำมันในjqตัวเอง xargsแต่วิธีที่ง่ายที่สุดที่ผมพบที่จะได้รับค่าทั้งสองต้องการในหนึ่งบรรทัดคือการใช้งาน ตัวอย่างเช่น:

$ jq 'select(.message.id == 1490) | .message.temperature, .message.humidity' raul.json | xargs
25.1 40

หรือถ้าคุณต้องการวนซ้ำแต่ละ.message.idอินสแตนซ์เราสามารถเพิ่ม.message.idเอาต์พุตและใช้xargs -n 3เมื่อเรารู้ว่าจะมีสามฟิลด์ (id, อุณหภูมิ, ความชื้น):

jq '.message.id, .message.temperature, .message.humidity' raul.json | xargs -n 3
4095 409.5 null
1490 25.1 40
2039 409.5 null

คุณสามารถโพสต์โปรเซสนั้นด้วย awk หรืออะไรก็ได้


ในที่สุดทั้งหลามและ Perl มีห้องสมุดที่ยอดเยี่ยมสำหรับการแยกและจัดการข้อมูล json เช่นเดียวกับภาษาอื่นหลายภาษารวมถึง php และ java


2
โดยเฉพาะjq 'select(.message.id == 1490) | .message.temperature, .message.humidity' raul.json
เกล็นแจ็คแมน

1
หรือในทุบตี{ read temp; read hum; } < <(jq ...)
เกล็นแจ็คแมน

1
grepดูคำตอบของฉันซึ่งก็ใช้ มันอาจไม่ทำงานสำหรับบางรุ่นgrepแต่มันตรงไปตรงมามากกว่าjqในสถานการณ์นี้แม้ว่าjqจะได้รับการออกแบบมาโดยเฉพาะสำหรับการแยกวิเคราะห์ JSON ฉันให้jqคำตอบ upvote แม้ว่าโดยไม่คำนึงถึง แน่นอนว่ามันเป็นเครื่องมือสำหรับงาน แต่บางครั้งคุณก็สามารถเอาลวดเย็บกระดาษออกได้ด้วยนิ้วของคุณแทนที่จะค้นหารอบ ๆ เพื่อหาลวดเย็บ
rubynorails

2
json ไม่สามารถแยกวิเคราะห์ได้อย่างน่าเชื่อถือด้วยการแสดงออกปกติใด ๆ เกินกว่า xml หรือ html สามารถ และข้อมูล json ส่วนใหญ่ (เช่นการดึงข้อมูลผ่านเว็บ API) ไม่ได้รับการจัดรูปแบบเป็นอย่างดีด้วยการป้อนบรรทัดและการเยื้องพิเศษ ในการแยกวิเคราะห์ json คุณต้องมี json parser jqเป็นหนึ่งสำหรับเชลล์สคริปต์ ภาษาอื่นมีห้องสมุด json ในการแยกวิเคราะห์
cas

1
สิ่งใดสามารถแยกวิเคราะห์ได้อย่างน่าเชื่อถือด้วยการแสดงออกปกติ ขึ้นอยู่กับจำนวนที่คุณใช้ คุณคิดว่าjqมันเป็นอย่างไร
mikeserv

0

jqคือทางออกที่งดงามที่สุด ด้วยawkคุณสามารถเขียน

awk -v id=1490 '
    $1 == "\"id\":" && $2 == id"," {matched = 1}
    $1 == "}," {matched = 0}
    matched && $1 ~ /temperature|humidity/ {sub(/,/,"", $2); print $2}
' file

0

สำหรับผู้ที่ไม่เข้าใจขั้นสูงawkเช่นเดียวกับที่พวกเขาต้องการ (เช่นคนอย่างฉัน) และไม่มีjqการติดตั้งไว้ล่วงหน้าวิธีแก้ปัญหาที่ง่ายคือการบีบคำสั่งพื้นฐานสองสามคำด้วยกันเช่น:

grep -A2 '"id": 1490,' stats.json | sed '/1490/d;s/"//g;s/,//;s/\s*//'

หากคุณเพียงแค่พยายามที่จะรับค่ามันง่ายกว่าการใช้grepมากกว่าawkหรือsed:

grep -A2 '"id": 1490,' stats.json | grep -o "[0-9]*\.[0-9]*"

เพื่อให้คำอธิบายดูเหมือนเป็นวิธีที่ง่ายที่สุดสำหรับฉัน

  • grep -A2คว้าเส้นที่คุณกำลังมองหาใน JSON พร้อมกับต่อไปนี้ 2 สายซึ่งมีอุณหภูมิและความชื้น
  • ไปป์grep -oเพียงพิมพ์เฉพาะตัวเลขที่คั่นด้วยเครื่องหมาย.(ซึ่งจะไม่เกิดขึ้นใน1490บรรทัดแรกดังนั้นคุณจึงเหลือ 2 ค่าของคุณ - อุณหภูมิและความชื้นง่ายมากแม้จะง่ายกว่าการใช้jqในความคิดของฉัน

0

เครื่องมือที่ฉันเลือกใช้สำหรับการประมวลผล JSON บนบรรทัดคำสั่งคือ jq อย่างไรก็ตามหากคุณไม่ได้ติดตั้ง jq คุณสามารถทำได้ดีกับ Perl:

# perl -MJSON -e '$/ = undef; my $data = <>; for my $hash (new JSON->incr_parse($data)) { my $msg = $hash->{message}; print "$msg->{temperature} $msg->{humidity}\n" if $msg->{id} == 1490 }' < data.json
25.1 40

0

เอาต์พุตของคุณคือชุดของตัวอย่าง JSON แทนที่จะเป็น JSON ที่สมบูรณ์ หาก / เมื่อคุณจัดเรียงเอาต์พุตของคุณใหม่ให้เป็น JSON แบบอินทิเกรตเช่นนี้ (สมมติว่าเอาต์พุตของคุณอยู่ในfile.json):

echo "[ $(cat file.json | sed -E 's/^}$/},/; $d') }]"

จากนั้นมันง่ายที่จะบรรลุสิ่งที่คุณต้องการด้วยjtcเครื่องมือ (ดูได้ที่: https://github.com/ldn-softdev/jtc ):

bash $ echo "[ $(cat file.json | sed -E 's/^}$/},/; $d') }]" | jtc -x "[id]:<1490>d [-1]" -y[temperature] -y[humidity] -l
"temperature": 25.1
"humidity": 40.0
bash $ 

ในตัวอย่างด้านบนลดลง-lหากคุณไม่ต้องการฉลากที่พิมพ์

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