2025, Oct 02 13:33
jsonpath-ng में [?()] प्रेडिकेट क्यों टूटता है और उपाय
जानें क्यों $.. के बाद JSONPath का [?()] प्रेडिकेट jsonpath-ng में पार्स नहीं होता, जबकि python-jsonpath इसे संभालता है. कारण, फर्क और समाधान देखें.
JSONPath से किसी फ़ील्ड पर JSON को फ़िल्टर करना कई बार जितना आसान लगता है उतना होता नहीं—खासकर जब अलग-अलग इम्प्लीमेंटेशन सिंटैक्स पर सहमत नहीं होते। अगर आप name के आधार पर कोई ऑब्जेक्ट चुनकर उसका label निकालना चाहते हैं, तो वही अभिव्यक्ति किसी एक टूल में चल सकती है लेकिन आपके Python कोड में असफल हो सकती है। यहां संक्षेप में बताया गया है कि $..[?(@.name=='is_literate')] जैसा फ़िल्टर jsonpath-ng में क्यों टूट जाता है और स्थिर परिणाम कैसे पाएं।
पुनरुत्पादन: ऑनलाइन पार्स होने वाला फ़िल्टर, लोकल पर विफल
उद्देश्य है उस ऑब्जेक्ट को खोजना जिसका name is_literate के बराबर हो, और फिर उसका label पढ़ना। इसके लिए अभिव्यक्ति है $..[?(@.name=='is_literate')]। यह किसी ऑनलाइन JSONPath टेस्टर में ठीक चलती है, लेकिन jsonpath-ng में पार्सर त्रुटि दे देती है।
from jsonpath_ng.ext import parse
expr_text = "$..[?(@.name=='is_literate')]"
compiled_query = parse(expr_text)
extracted = [hit.value for hit in compiled_query.find(source_payload)]
print(extracted)
त्रुटि प्रश्नचिह्न टोकन की ओर इशारा करती है, यानी पार्सर इस सिंटैक्स को स्वीकार नहीं करता।
प्रेडिकेट में समस्या क्या है
मूल कारण है फ़िल्टर प्रेडिकेट का सिंटैक्स [?()]। jsonpath-ng में, ऊपर दर्शाए गए संदर्भ में यह रूप स्वीकार नहीं होता और पार्सर ? टोकन पर विफल हो जाता है। इस व्यवहार से जुड़े कुछ अवलोकन यह हैं कि recursive descent ऑपरेटर के तुरंत बाद फ़िल्टर रखना समस्याजनक हो सकता है, जबकि पैकेज डॉक्यूमेंटेशन में उदाहरण आम तौर पर $.objects[?(@some_field > 5)] जैसे पैटर्न का अनुसरण करते हैं, जहां फ़िल्टर से पहले एक नामित सेगमेंट होता है। $..*[?(@name=='is_literate')] जैसी कोशिशें पार्स त्रुटि से तो बचाती हैं, पर अपेक्षित परिणाम फिर भी नहीं देतीं। कुछ परीक्षणों में $[?(@.name=='is_literate')] या यहां तक कि $[?name=='is_literate'] जैसी अभिव्यक्तियां परिणाम लौटा सकती हैं, लेकिन $.. और [?()] वाली मूल संरचना नहीं। संक्षेप में, jsonpath-ng का पार्सर इस उपयोग में ? सिंटैक्स का समर्थन नहीं करता।
समाधान: ऐसी लाइब्रेरी अपनाएं जो इस फ़िल्टर रूप को संभालती हो
jsonpath-ng से python-jsonpath पर जाने से समस्या दूर हो जाती है। python-jsonpath में वही प्रेडिकेट अभिव्यक्ति अपेक्षित रूप से चलती है और लक्षित ऑब्जेक्ट लौटाती है, जिससे आप ज्ञात name के आधार पर उसका label पढ़ सकते हैं।
import jsonpath
matched_nodes = jsonpath.findall(
    "$..[?(@.name=='is_literate')]",
    source_payload
)
यह उस ऑब्जेक्ट वाला अपेक्षित एरे देता है जिसका name is_literate है, और आगे उसकी label वैल्यू तक पहुंच बन जाती है।
यह क्यों मायने रखता है
ऊपरी तौर पर JSONPath मानकीकृत लगता है, लेकिन अलग-अलग लाइब्रेरीज़ पार्सिंग और फ़िल्टर के मूल्यांकन में भिन्न होती हैं। इस मामले में ? वाला प्रेडिकेट एक टूल में चलता है और दूसरे में असफल हो जाता है। जब आपका उपयोग-परिदृश्य किसी ज्ञात name से जुड़ा label जैसा सिबलिंग निकालने के लिए फ़ील्ड-आधारित फ़िल्टरिंग पर टिका हो, तो यही बारीक अंतर एक लाइन के समाधान और पार्सर त्रुटि के बीच फ़र्क तय कर देते हैं।
निष्कर्ष
अगर आपको name के आधार पर फ़िल्टर करके label चाहिए और आपकी अभिव्यक्ति $..[?(@.name=='is_literate')] जैसी है, तो मानकर चलें कि jsonpath-ng प्रेडिकेट पर टूटेगा। या तो अपनी क्वेरी को ऐसे रूप में ढालें जो आपके डेटा-स्ट्रक्चर के लिए पार्स हो सके, या फिर python-jsonpath अपनाएं, जो इस संदर्भ में प्रेडिकेट फ़िल्टर स्वीकार करता है और वांछित ऑब्जेक्ट लौटाता है। शुरुआत में ही लाइब्रेरी के समर्थित सिंटैक्स से परिचित रहना उन अभिव्यक्तियों पर समय बचा देता है जो “ऑनलाइन तो चलती हैं”, पर आपके प्रोडक्शन कोड में नहीं।
यह लेख StackOverflow के प्रश्न पर आधारित है, जिसे Lalit mahato ने पूछा था, और उसी के उत्तर पर, जो Lalit mahato द्वारा दिया गया था।