给定具有路径的数组,在JSON对象中设置值

时间:2015-10-23 19:48:20

标签: arrays json coffeescript

给定一个JSON对象:{"a": {"b": [{"c": "123"}]}}和一个包含JSON ["a", "b", "0", "c"]路径的数组,如何修改“123”的最终值?

json["a"]["b"][0]["c"] = "abc"是我想要完成的,但带路径的数组是完全可变的。路径数组中的每个项目将始终位于JSON对象中。

我正在使用CoffeeScript,但psuedo代码同样有用

3 个答案:

答案 0 :(得分:3)

您需要使用除最后一个路径项之外的所有项目钻取对象。然后使用最后一个路径项来设置值。在JavaScript中,您可以创建以下函数来设置值...

function setValue(obj, path, value) {
    var i;
    for (i = 0; i < path.length - 1; i++) {
        obj = obj[path[i]];
    }
    obj[path[i]] = value;
}

然后您可以使用...

调用该函数
var obj = {"a": {"b": [{"c": "123"}]}};
var path = ["a", "b", "0", "c"];
var value = "abc";
setValue(obj, path, value);

答案 1 :(得分:0)

我改进了Bobby的答案并用CoffeeScript编写了它。效果很好。

setValue = (obj, path, value) ->
  path.forEach (val, index, path) ->
    if path.length - 1 != index 
      obj = obj[val]
    else
      obj[val] = value

json = "a": "b": ["c": "123"]
path = ["a", "b", "0", "c"]
value = "abc"

setValue json, path, value

console.log json["a"]["b"][0]["c"]  # => "abc"

我希望它对你有所帮助。

答案 2 :(得分:0)

一个更通用,更复杂的答案怎么样?这就是我在excel-as-json中使用的内容。请参阅下面的链接或测试用例,了解json点字符串键路径语法的示例。

BOOLTEXT = ['true', 'false']
BOOLVALS = {'true': true, 'false': false}

isArray = (obj) ->
  Object.prototype.toString.call(obj) is '[object Array]'

# Extract key name and array index from names[1] or names[]
# return [keyIsList, keyName, index]
# for names[1] return [true,  keyName,  index]
# for names[]  return [true,  keyName,  undefined]
# for names    return [false, keyName,  undefined]
parseKeyName = (key) ->
  index = key.match(/\[(\d+)\]$/)
  switch
    when index             then [true, key.split('[')[0], Number(index[1])]
    when key[-2..] is '[]' then [true, key[...-2], undefined]
    else                        [false, key, undefined]

# Convert a list of values to a list of more native forms
convertValueList = (list) ->
  (convertValue(item) for item in list)

# Convert values to native types
# Assume: all values from the excel module are text
convertValue = (value) ->
  if isFinite(value)
    Number(value)
  else
    testVal = value.toLowerCase()
    if testVal in BOOLTEXT
      BOOLVALS[testVal]
    else
      value

# Assign a value to a dotted property key - set values on sub-objects
assign = (obj, key, value) ->
  # On first call, a key is a string. Recursed calls, a key is an array
  key = key.split '.' unless typeof key is 'object'
  # Array element accessors look like phones[0].type or aliases[]
  [keyIsList, keyName, index] = parseKeyName key.shift()

  if key.length
    if keyIsList
      # if our object is already an array, ensure an object exists for this index
      if isArray obj[keyName]
        unless obj[keyName][index]
          obj[keyName].push({}) for i in [obj[keyName].length..index]
      # else set this value to an array large enough to contain this index
      else
        obj[keyName] = ({} for i in [0..index])
      assign obj[keyName][index], key, value
    else
      obj[keyName] ?= {}
      assign obj[keyName], key, value
  else
    if keyIsList and index?
      console.error "WARNING: Unexpected key path terminal containing an indexed list for <#{keyName}>"
      console.error "WARNING: Indexed arrays indicate a list of objects and should not be the last element in a key path"
      console.error "WARNING: The last element of a key path should be a key name or flat array. E.g. alias, aliases[]"
    if (keyIsList and not index?)
      obj[keyName] = convertValueList(value.split ';')
    else
      obj[keyName] = convertValue value

# Test cases
obj = {}
assign obj, 'firstName', 'Jihad'
assign obj, 'lastName', 'Saladin'
assign obj, 'address.street', '12 Beaver Court'
assign obj, 'address.city', 'Snowmass'
assign obj, 'address.state', 'CO'
assign obj, 'address.zip', '81615'
assign obj, 'isEmployee', 'true'
assign obj, 'phones[0].type', 'home'
assign obj, 'phones[0].number', '123.456.7890'
assign obj, 'phones[1].type',  'work'
assign obj, 'phones[1].number', '098.765.4321'
assign obj, 'aliases[]', 'stormagedden;bob'
console.log JSON.stringify obj, null, 2

产生

{
  "firstName": "Jihad",
  "lastName": "Saladin",
  "address": {
    "street": "12 Beaver Court",
    "city": "Snowmass",
    "state": "CO",
    "zip": 81615
  },
  "isEmployee": true,
  "phones": [
    {
      "type": "home",
      "number": "123.456.7890"
    },
    {
      "type": "work",
      "number": "098.765.4321"
    }
  ],
  "aliases": [
    "stormagedden",
    "bob"
  ]
}