我有一个这样的数据结构:

var someObject = {
    'part1' : {
        'name': 'Part 1',
        'size': '20',
        'qty' : '50'
    },
    'part2' : {
        'name': 'Part 2',
        'size': '15',
        'qty' : '60'
    },
    'part3' : [
        {
            'name': 'Part 3A',
            'size': '10',
            'qty' : '20'
        }, {
            'name': 'Part 3B',
            'size': '5',
            'qty' : '20'
        }, {
            'name': 'Part 3C',
            'size': '7.5',
            'qty' : '20'
        }
    ]
};

我想使用这些变量访问数据:

var part1name = "part1.name";
var part2quantity = "part2.qty";
var part3name1 = "part3[0].name";

part1name应该用someObject.part1.name的值填充,即“Part 1”。part2quantity也是一样,它的容量是60。

有没有办法实现这与纯javascript或JQuery?


当前回答

这是我使用的解决方案:

function resolve(path, obj=self, separator='.') {
    var properties = Array.isArray(path) ? path : path.split(separator)
    return properties.reduce((prev, curr) => prev?.[curr], obj)
}

使用示例:

// accessing property path on global scope
resolve("document.body.style.width")
// or
resolve("style.width", document.body)

// accessing array indexes
// (someObject has been defined in the question)
resolve("part3.0.size", someObject) // returns '10'

// accessing non-existent properties
// returns undefined when intermediate properties are not defined:
resolve('properties.that.do.not.exist', {hello:'world'})

// accessing properties with unusual keys by changing the separator
var obj = { object: { 'a.property.name.with.periods': 42 } }
resolve('object->a.property.name.with.periods', obj, '->') // returns 42

// accessing properties with unusual keys by passing a property name array
resolve(['object', 'a.property.name.with.periods'], obj) // returns 42

限制:

不能将方括号([])用于数组下标-尽管在分隔符令牌之间指定数组下标(例如,.)可以正常工作,如上所示。

其他回答

我认为你的要求是:

var part1name = someObject.part1.name;
var part2quantity = someObject.part2.qty;
var part3name1 =  someObject.part3[0].name;

你可以这样问:

var part1name = someObject["part1"]["name"];
var part2quantity = someObject["part2"]["qty"];
var part3name1 =  someObject["part3"][0]["name"];

这两种方法都可行


也许这是你自找的

var partName = "part1";
var nameStr = "name";

var part1name = someObject[partName][nameStr];

终于轮到你自找麻烦了

var partName = "part1.name";

var partBits = partName.split(".");

var part1name = someObject[partBits[0]][partBits[1]];

使用对象扫描,这就变成了一行。然而,更重要的是,这个解决方案考虑性能:

在搜索期间遍历一次输入(即使查询了多个键) 解析只在init上发生一次(如果查询多个对象) 允许使用*进行扩展语法

// const objectScan = require('object-scan'); const someObject = { part1: { name: 'Part 1', size: '20', qty: '50' }, part2: { name: 'Part 2', size: '15', qty: '60' }, part3: [{ name: 'Part 3A', size: '10', qty: '20' }, { name: 'Part 3B', size: '5', qty: '20' }, { name: 'Part 3C', size: '7.5', qty: '20' }] }; const get = (haystack, needle) => objectScan([needle], { rtn: 'value', abort: true })(haystack); console.log(get(someObject, 'part1.name')); // => Part 1 console.log(get(someObject, 'part2.qty')); // => 60 console.log(get(someObject, 'part3[0].name')); // => Part 3A const getAll = (haystack, ...needles) => objectScan(needles, { reverse: false, rtn: 'entry', joined: true })(haystack); console.log(getAll(someObject, 'part1.name', 'part2.qty', 'part3[0].name')); /* => [ [ 'part1.name', 'Part 1' ], [ 'part2.qty', '60' ], [ 'part3[0].name', 'Part 3A' ] ] */ console.log(getAll(someObject, 'part1.*')); /* => [ [ 'part1.name', 'Part 1' ], [ 'part1.size', '20' ], [ 'part1.qty', '50' ] ] */ .as-console-wrapper {max-height: 100% !important; top: 0} <script src="https://bundle.run/object-scan@13.8.0"></script>

声明:我是object-scan的作者

根据Alnitak的回答:

if(!Object.prototype.byString){
  //NEW byString which can update values
Object.prototype.byString = function(s, v, o) {
  var _o = o || this;
      s = s.replace(/\[(\w+)\]/g, '.$1'); // CONVERT INDEXES TO PROPERTIES
      s = s.replace(/^\./, ''); // STRIP A LEADING DOT
      var a = s.split('.'); //ARRAY OF STRINGS SPLIT BY '.'
      for (var i = 0; i < a.length; ++i) {//LOOP OVER ARRAY OF STRINGS
          var k = a[i];
          if (k in _o) {//LOOP THROUGH OBJECT KEYS
              if(_o.hasOwnProperty(k)){//USE ONLY KEYS WE CREATED
                if(v !== undefined){//IF WE HAVE A NEW VALUE PARAM
                  if(i === a.length -1){//IF IT'S THE LAST IN THE ARRAY
                    _o[k] = v;
                  }
                }
                _o = _o[k];//NO NEW VALUE SO JUST RETURN THE CURRENT VALUE
              }
          } else {
              return;
          }
      }
      return _o;
  };

}

这也允许你设置一个值!

我已经用这个创建了一个npm包和github

从@Alnitak answer开始,我构建了这个源代码,它下载了一个实际的.JSON文件并对其进行处理,为每一步打印到控制台解释字符串,以及在传递错误键的情况下的更多细节:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  <script>
function retrieveURL(url) {
        var client = new XMLHttpRequest();
        prefix = "https://cors-anywhere.herokuapp.com/"
        client.open('GET', prefix + url);
        client.responseType = 'text';
        client.onload = function() {
            response = client.response; // Load remote response.
            console.log("Response received.");
            parsedJSON  = JSON.parse(response);
            console.log(parsedJSON);
            console.log(JSONitemByPath(parsedJSON,"geometry[6].obs[3].latituade"));
            return response;
        };
        try {
            client.send();
        } catch(e) {
            console.log("NETWORK ERROR!");
            console.log(e);
        }
}



function JSONitemByPath(o, s) {
    structure = "";
    originalString = s;
    console.log("Received string: ", s);
    s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
    console.log("Converted to   : ", s);
    s = s.replace(/^\./, '');           // strip a leading dot
    var a = s.split('.');

    console.log("Single keys to parse: ",a);

    for (var i = 0, n = a.length; i < n; ++i) {
        var k = a[i];
        if (k in o) {
            o = o[k];
            console.log("object." + structure +  a[i], o);
            structure +=  a[i] + ".";
        } else {
            console.log("ERROR: wrong path passed: ", originalString);
            console.log("       Last working level: ", structure.substr(0,structure.length-1));
            console.log("       Contents: ", o);
            console.log("       Available/passed key: ");
            Object.keys(o).forEach((prop)=> console.log("       "+prop +"/" + k));
            return;
        }
    }
    return o;
}


function main() {
    rawJSON = retrieveURL("http://haya2now.jp/data/data.json");
}

</script>
  </head>
  <body onload="main()">
  </body>
</html>

输出的例子:

Response received.
json-querier.html:17 {geometry: Array(7), error: Array(0), status: {…}}
json-querier.html:34 Received string:  geometry[6].obs[3].latituade
json-querier.html:36 Converted to   :  geometry.6.obs.3.latituade
json-querier.html:40 Single keys to parse:  (5) ["geometry", "6", "obs", "3", "latituade"]
json-querier.html:46 object.geometry (7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]
json-querier.html:46 object.geometry.6 {hayabusa2: {…}, earth: {…}, obs: Array(6), TT: 2458816.04973593, ryugu: {…}, …}
json-querier.html:46 object.geometry.6.obs (6) [{…}, {…}, {…}, {…}, {…}, {…}]
json-querier.html:46 object.geometry.6.obs.3 {longitude: 148.98, hayabusa2: {…}, sun: {…}, name: "DSS-43", latitude: -35.4, …}
json-querier.html:49 ERROR: wrong path passed:  geometry[6].obs[3].latituade
json-querier.html:50        Last working level:  geometry.6.obs.3
json-querier.html:51        Contents:  {longitude: 148.98, hayabusa2: {…}, sun: {…}, name: "DSS-43", latitude: -35.4, …}
json-querier.html:52        Available/passed key: 
json-querier.html:53        longitude/latituade
json-querier.html:53        hayabusa2/latituade
json-querier.html:53        sun/latituade
json-querier.html:53        name/latituade
json-querier.html:53        latitude/latituade
json-querier.html:53        altitude/latituade
json-querier.html:18 undefined

AngularJS

Speigg的方法非常简洁,尽管我在搜索通过字符串路径访问AngularJS $scope属性的解决方案时发现了这个回复,并进行了一些修改:

$scope.resolve = function( path, obj ) {
    return path.split('.').reduce( function( prev, curr ) {
        return prev[curr];
    }, obj || this );
}

只需要把这个函数放在你的根控制器中,并像这样使用它的任何子范围:

$scope.resolve( 'path.to.any.object.in.scope')