3

Example : Currently we can refer name property value in address name property as below

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

Is there any way to refer same with relative path like below.

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

With Above question what I am looking at is an easy way to refer any value in current json where there are complex and multiple hierarchies as given below in first code snippet. For referring “Name” property from sibling I have to mention a complete path right from the root, which can be though to maintain. If there is small change in hierarchy structure the reference will not be valid anymore.

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

If we could able to refer the same property as given in second snippet then change in upper hierarchy will not have any impact on reference, There will be only change when there is direct change in the sibling of “FiveLevel1” and “FiveLevel2”

{
  "one": {
    "two": {
      "three": {
        "four": {
          "five": {
            "fiveLevel1": {
              "name": "foo"
            },
            "fiveLevel2": {
              "name": {
                "$ref": "#../fiveLevel1/name"
              }
            }
          }
        }
      }
    }
  }
} 
Atul
  • 3,013
  • 2
  • 12
  • 15
  • As far as I'm aware, the concept of references does not exist within JSON. You can still symbolically represent references, as you have done here, but that symbolism is only important to you, not to the JSON itself. – Gershom Maes Mar 05 '19 at 20:22
  • @GershomMaes There are many embeddings in JSON that do support it – Bergi Mar 05 '19 at 20:53
  • 2
    I think you are looking for https://tools.ietf.org/html/rfc6901 – Bergi Mar 05 '19 at 20:53
  • 1
    What API are you using exactly that supports these `$ref` keys? Maybe some of these tutorials will help you: https://swagger.io/docs/specification/using-ref/ https://cswr.github.io/JsonSchema/spec/definitions_references/ – Bergi Mar 05 '19 at 20:58
  • Right, there may be tools which accept a subset of JSON and interpret certain values as references. – Gershom Maes Mar 05 '19 at 21:10
  • Thanks @Bergi for updating the question. I have added few more details to question so it will be easy to understand. – Atul Mar 07 '19 at 15:36
  • @Atul I think it already was easy to understand what you want. The detail I am still missing in the question text is which service/API/library you are using that supports references in JSON - the answer whether it also supports relative pointers may depend on that. – Bergi Mar 07 '19 at 15:41
  • 1
    I tried below below answers from stackoverflow and libraries like Douglas Crockfords Plugin and json-schema-ref-parser package. https://stackoverflow.com/questions/15312529/resolve-circular-references-from-json-object/15757499 https://github.com/douglascrockford/JSON-js https://www.npmjs.com/package/json-schema-ref-parser – Atul Mar 07 '19 at 16:14

2 Answers2

1

There is a draft Relative JSON Pointers which unfortunately exipred in the Aug, 2021 and I am not sure if there is any later extension.

Nevertheless, it should be a good start as it is an extension of the already mentioned RFC6901 - JavaScript Object Notation (JSON) Pointer

mPrinC
  • 9,147
  • 2
  • 32
  • 31
0

This feature doesn't exist in vanilla JavaScript, as well stated in the comments.

However, nothing stops you from adding circular references to your objects, so that you can achieve something close to what you're looking for. You could even wrap this up in a class:

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);

It's far, far from being perfect, but the main idea here is to loop through the object's properties adding a reference to the parent inside every child, and then repeating this recursively. When it finds $ref, it parses the path and translates it to its value.

Of course, if the object hierarchy changes, you'd then have to fix the parent references (it's something you could implement in your custom class).

Remember: this is just a simple code snippet to show you an idea of what you could do, I didn't put a lot of thought in it. Don't use it in production.

Pedro Corso
  • 557
  • 8
  • 22