Javascript/JSON 如何获取给定子节点的路径?

14

如何获得对象中给定子节点的JSON路径?

例如:

var data = {
    key1: {
        children: {
            key2:'value',
            key3:'value',
            key4: { ... }
        }, 
    key5: 'value'
}

给定一个引用key4的变量,现在我正在寻找绝对路径:

data.key1.children.key4

有没有办法在JS中完成这个任务?

提前感谢。


如果您的搜索变量指向字符串对象(如示例中),则将无法可靠地搜索该路径,因为(例如)data.key1.children.key3 === data.key4 也会是 true,这可能不是您想要实现的。 - Yoshi
谢谢Yoshi,我已经更新了代码示例。现在引用指向另一个对象。 - user1138959
6个回答

12

所以你有一个值为"key3"的变量,并且你想知道如何根据这个字符串的值动态访问该属性?

var str = "key3";
data["key1"]["children"][str];

编辑

哇,我真不敢相信我第一次尝试就搞定了。这个程序可能存在一些错误,但它已经可以处理你的测试用例了。

在线演示

var x = data.key1.children.key4;

var path = "data";
function search(path, obj, target) {
    for (var k in obj) {
        if (obj.hasOwnProperty(k))
            if (obj[k] === target)
                return path + "['" + k + "']"
            else if (typeof obj[k] === "object") {
                var result = search(path + "['" + k + "']", obj[k], target);
                if (result)
                    return result;
            }
    }
    return false;
}

var path = search(path, data, x);
console.log(path); //data['key1']['children']['key4']

1
嗨,亚当,谢谢你的回复。我正在寻找完整路径。 "key1" 和 "children" 是未知的。我有一个存储在变量中的 data.key1.children.key3 的引用,需要知道如何到达那里。 - user1138959
@user1138959 - 我不确定这是可能的。我的意思是,你可以通过递归搜索对象的每个成员来实现,但如果你有两个相同的值,仍然无法知道你的变量指向哪个成员。 - Adam Rackis
1
我已经更新了我的代码示例。我正在寻找一个对象,而不是一个字符串值。如何递归地搜索每个成员并获取匹配子项的路径? - user1138959
@user1138959 - 正如lwburk所提到的,确保你没有像children: { ... key6: data.key1这样的东西 - 除非你真的喜欢无限循环 ;) - Adam Rackis

10

这是我完成此任务的方法。

/**
 * Converts a string path to a value that is existing in a json object.
 * 
 * @param {Object} jsonData Json data to use for searching the value.
 * @param {Object} path the path to use to find the value.
 * @returns {valueOfThePath|null}
 */
function jsonPathToValue(jsonData, path) {
    if (!(jsonData instanceof Object) || typeof (path) === "undefined") {
        throw "Not valid argument:jsonData:" + jsonData + ", path:" + path;
    }
    path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
    path = path.replace(/^\./, ''); // strip a leading dot
    var pathArray = path.split('.');
    for (var i = 0, n = pathArray.length; i < n; ++i) {
        var key = pathArray[i];
        if (key in jsonData) {
            if (jsonData[key] !== null) {
                jsonData = jsonData[key];
            } else {
                return null;
            }
        } else {
            return key;
        }
    }
    return jsonData;
}  

用于测试

var obj = {d1:{d2:"a",d3:{d4:"b",d5:{d6:"c"}}}};
jsonPathToValue(obj, "d1.d2"); // a 
jsonPathToValue(obj, "d1.d3"); // {d4: "b", d5: Object}
jsonPathToValue(obj, "d1.d3.d4"); // b
jsonPathToValue(obj, "d1.d3.d5"); // {d6: "c"}
jsonPathToValue(obj, "d1.d3.d5.d6"); // c

希望这能帮到某些人。


谢谢你提供这段代码,正是我所需要的。但是有一些小错误:如果路径中没有找到键,则它不会返回 undefined,而是返回 null 或路径中的一个键。只需将 return nullreturn key 替换即可! - Massale

1

我的解决方案:

  • 基于给定参考点的深度优先搜索
  • 使用递归
  • 处理数组

(可以使用此deep_value函数检索结果。)

var key4Ref = { abc: 123 }

var data = {
    key1: {
        children: {
            key2:'value',
            key3:'value',
            key4: key4Ref
        }, 
        key5: 'value'
    }
}

// find the path to a 'ref' within an object 'data'.
const pathTo = (ref, data, path = []) => {
  const found = data && Object.entries(data).find(([k,v]) => {
    if (v === ref) return path.push(k)
    if (typeof v === 'object') {
      const tmp = pathTo(ref, v, [...path, k])
      if (tmp) return path = tmp
    }
  })
  if (found) return path
}

console.log(pathTo(key4Ref, data).join('.'))


0

var data = {
  // Your data is here
  a: {
    b: {
      c: {
        d: "Assalamu alal muslimin"
      }
    }
  },
  // Your function is here
  take: function(path) {
    var temp = this; // take a copy of object
    if(!path) return temp; // if path is undefined or empty return the copy
    path = path.split("/");
    for(var p in path) {
      if(!path[p]) continue; // means "a/" = "a"
      temp = temp[path[p]]; // new data is subdata of data
      if(!temp) return temp; 
    }
    return temp;
  }
};
<input placeholder="Please enter the path"/>
<button onclick="document.querySelector('div').innerText = JSON.stringify(data.take(document.querySelector('input').value))">
  Try it
</button>
<br><br>
Data: {a:{b:{c:{d:"Assalamu alal muslimin"}}}}
<br><br>
Code: data.take(path)
<br><br>
Result:
<div></div>

这个函数的作用简要地说:

function getDataByPath(data, path) {
    if(!path) return data; // if path is undefined or empty return data
    path = path.split("/");
    for(var p in path) {
        if(!path[p]) continue; // "a/" = "a"
      . data = data[path[p]]; // new data is subdata of data
        if(!data) return data; // "a/b/d" = undefined
    }
    return data;
}

这是最短的函数,但如果您输入了错误的路径,它可能会出现错误:

function getDataByPath(data, path) {
  for(var i in path.split("/")) data = data[path[i]];
  return data;
}

0

我也在尝试解决这个问题,JSON路径中也包含数组,这是我最终想出的方法:

function findValuePath(obj, value) {
    // Initialize the array of results and the array of paths
    let result = [];
    let path = [];

    // Recursive functions look up values
    function searchValue(obj, value) {
        for (let key in obj) {
            // If the current attribute value is equal to the target value, the path is logged
            if (obj[key] === value) {
                path.push((Array.isArray(obj) ? `[${key}]` : `.${key}`));
                result = path.slice();
                path.pop();
            }
            // If the current property is an object or array, search recursively
            else if (typeof obj[key] === 'object') {
                path.push((Array.isArray(obj) ? `[${key}]` : `.${key}`));
                searchValue(obj[key], value);
                path.pop();
            }
        }
    }

    // Call the recursive function
    searchValue(obj, value);

    //If the target value is found, the path string is returned, otherwise an empty string is returned
    return result.length > 0 ? result.join('') : '';
}

这是我制作的测试示例:

let obj = {
    a:1,
    b:"hello",
    c:{
        a:"target000",
        b:"tar",
        c:"target_w",
        d:[
            "target0",
            "target1",
            "target2",
            {
                a:2,
                b:"target"
            }
        ]
    }
}
let res = findValuePath(obj,"target")
console.log(res)                   // ".c.d[3].b"
console.log(`obj${res}`)           // "obj.c.d[3].b"
console.log(eval(`obj${res}`))     // "target"

0

let x;
try{
  x = JSON.parse(prompt("Input your JSON"))
}
catch(e) {
   alert("not a valid json input")
}
var res = {};
var constructResultCurry = function(src){ return constructResult(res,src); }
        
function constructResult(target, src) {
  if(!src) return;
  target[src.key] = src.val;
}
        
function buildPath(key, obj, overAllKey) {
  overAllKey += (overAllKey ? "." : "") + key;
  if(typeof obj[key] != "object") return { key : overAllKey, val : obj[key] };
  Object.keys(obj[key]).forEach(function(keyInner) {
     constructResultCurry(buildPath(keyInner, obj[key], overAllKey));  
  });
}
        
Object.keys(x).forEach(function(k){
  constructResultCurry(buildPath(k, x, ""));
});
console.log("**************ALL FIELDS****************")
console.log(res);
console.log("******************************************")

let conf = confirm("do you need a specific field from JSON");
if ( conf )
{
   let field = prompt("Input field name")
   let results = Object.fromEntries(
  Object.entries(res).filter(([key]) => (key.toLowerCase()).includes((field.toLowerCase()))))
  prompt("Copy to clipboard: Ctrl+C, Enter", JSON.stringify(results));
   console.log(results)
   
} 
else {
   prompt("Copy to clipboard: Ctrl+C, Enter", JSON.stringify(res));
}

上述解决方案返回整个JSON,其中包含每个字段的完整路径,以及请求的特定字段的路径。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接