• בלוג
  • מעדכנים אוביקט JSON עם JSONPath

מעדכנים אוביקט JSON עם JSONPath

15/05/2024

נניח שיש לכם אוביקט JSON מקונן למשל את זה:

const data = {
  rows: [
    {id: '1', cells: [{text: 'a', state: { active: true }},
                      {text: 'b'}]},
    {id: '2', cells: [{text: 'c'}]}
  ]
}

ואתם רוצים לעדכן אותו כך שה state של כל התאים יקבל ערך false, כן גם אלה שלא מוגדר להם ערך עבור המפתח state. המחשבה הראשונה שלכם עשויה להיות לולאה כפולה:

for (let row of data.rows) {
    for (let cell of row.cells) {
    if (!cell.state) { cell.state = {} }
        cell.state.active = false
  }
} 

וזה עובד. זמנית. כי מהר מאוד האוביקט יקבל שורה בלי cells למשל:

const data = {
  rows: [
    {id: '1', cells: [{text: 'a', state: { active: true }},
                      {text: 'b'}]},
    {id: '2', cells: [{text: 'c'}]},
    {id: '3' }
  ]
}

ועכשיו לך תזכור איפה הטעות.

גישה יותר גמישה היא JSONPath. זה spec שמאפשר לחפש מידע באוביקט JSON מקונן או לשנות שדות בתוך אותו אוביקט. הספריה jsonpath מ npm כוללת פונקציות לעבודה עם נתיבים בתוך JSON-ים ואפשר לשלב אותה ביישומי node.js או בדפדפן. הדוגמה שלהם מתוך דף התיעוד היא:

var cities = [
  { name: "London", "population": 8615246 },
  { name: "Berlin", "population": 3517424 },
  { name: "Madrid", "population": 3165235 },
  { name: "Rome",   "population": 2870528 }
];

var jp = require('jsonpath');
var names = jp.query(cities, '$..name');

// [ "London", "Berlin", "Madrid", "Rome" ]

המחרוזת $..name היא נתיב לכל הערכים שמתאימים למפתח בשם name בכל עומק של האוביקט. יש להם גם טבלה בדף התיעוד עם כל הסימנים שאפשר לרשום בנתיב JSON. בחזרה לאוביקט שלנו עם השורות והתאים בעזרת JSONPath אפשר לפתור את הבעיה בצורה יותר פשוטה וגנרית:

function desactivateAll() {
  jsonpath.apply(data, '$.rows[*].cells[*]', (v) => v.state ? v : Object.assign(v, {state: { active: false }}))
  // OR less "safe way":
  // jsonpath.apply(data, '$...active', (v) => false);
  jsonpath.apply(data, '$.rows[*].cells[*].state.active', (v) => false);
}

הפונקציה apply מקבלת אוביקט, נתיב JSONPath ופונקציית עדכון ומעדכנת את הערכים שמתאימים לנתיב לפי פונקציית העדכון. הקוד הזה לא נבהל כשמוסיפים שורה בלי cells - אותו אוביקט פשוט יישאר ללא שינוי.

כתבתי גם דוגמה מלאה בקודפן בקישור הזה כדי שיהיה לכם קל לשחק עם הספריה: https://codepen.io/ynonp/pen/eYamWYp