有什么方法可以使用JSON指针在侧面JSON字符串中使用相对路径?

时间:2019-01-31 18:25:52

标签: javascript json jsonpointer

示例: 目前,我们可以在地址名称属性中引用名称属性值,如下所示

{
        person: { 
            name: "a",
            address: {
                name: { "$ref":"#/person/name"},
                zip:"123"
            }
        }
  }

有没有办法用相对路径引用相同的内容,如下所示。

{
        person: { 
            name: "a",
            address: {
                name: { "$ref":"#/../name"},
                zip:"123"
            }
        }
  }

通过上面的问题是什么,我看到的是在当前的JSON那里是复杂的,多层次,如下面第一个代码段给出的参考值,任何一种简单的方法。为了从兄弟姐妹那里引用“ Name”属性,我必须从根开始提到一个完整的路径,尽管可以维护。如果层次结构中的更改很小,则该引用将不再有效。

{
  "one": {
    "two": {
      "three": {
        "four": {
          "five": {
            "fiveLevel1": {
              "name": "foo"
            },
            "fiveLevel2": {
              "name": {
                "$ref": "#/one/two/three/four/five/fiveLevel1/name"
              }
            }
          }
        }
      }
    }
  }
}

如果我们能够引用第二个片段中给出的相同属性,那么上层层次的更改将不会对引用产生任何影响,只有在“ FiveLevel1”和“ FiveLevel2”的同级中有直接更改时,更改才会发生“

{
  "one": {
    "two": {
      "three": {
        "four": {
          "five": {
            "fiveLevel1": {
              "name": "foo"
            },
            "fiveLevel2": {
              "name": {
                "$ref": "#../fiveLevel1/name"
              }
            }
          }
        }
      }
    }
  }
} 

1 个答案:

答案 0 :(得分:0)

香草JavaScript中不存在此功能,注释中对此也有说明。

但是,没有什么可以阻止您向对象添加循环引用,从而可以实现与所需内容接近的功能。您甚至可以将其包装在一个类中:

class RelPathObj {
  constructor(obj) {
    function build (parent, parentKey, obj) {
      if (typeof obj === "object") {
        for (let key in obj) {
          if (typeof obj[key] === "object") {
            parent[key] = { "..": parent, ...obj[key] };
            build(parent[key], key, obj[key]);
          } else {
            if (key === "$ref") {
              const path = obj[key].split("/");
              Object.defineProperty(parent[".."],
                                    parentKey,
                                    {
                                      get: function() {
                                        let value = parent[".."];
                                        path.forEach(p => { value = value[p] });
                                        return value;
                                      },
                                      set: function(value) {
                                        let ref = parent[".."];
                                        path.forEach(p => { 
                                          if (typeof ref[p] === "object")
                                            ref = ref[p] 
                                          else
                                            ref[p] = value
                                        });
                                      }
                                    });
            } else {
              parent[key] = obj[key];
            }
          }
        }
      } else {
        parent = obj;
      }
    }
    this.root = {};
    build(this.root, "root", obj);
  } 
}

const relativePathObject = new RelPathObj({
  "one": {
    "two": {
      "three": {
        "four": {
          "five": {
            "fiveLevel1": {
              "name": "foo"
            },
            "fiveLevel2": {
              "name": {
                "$ref": "../fiveLevel1/name"
              }
            }
          }
        }
      }
    }
  }
});

let fiveLevel1 = relativePathObject["root"]["one"]["two"]["three"]["four"]["five"]["fiveLevel1"];
console.log(fiveLevel1.name);
  
let fiveLevel2 = fiveLevel1[".."]["fiveLevel2"];
console.log(fiveLevel2.name);
  
fiveLevel1.name = "bar";
console.log(fiveLevel2.name);
  
fiveLevel2.name = "baz"
console.log(fiveLevel1.name);

距离完美还很远,但是这里的主要思想是遍历对象的属性,在每个孩子中添加对父对象的引用,然后递归地重复此操作。找到$ref时,它将解析路径并将其转换为其值。

当然,如果对象层次结构发生变化,则必须修复父级引用(可以在自定义类中实现)。

记住:这只是一个简单的代码段,向您展示了您可以做什么的想法,但我并没有考虑太多。 请勿在生产中使用它