首页 > 解决方案 > 用覆盖部分替换 json 键

问题描述

我有一个配置 json 文件,这是一个示例

{
  "version": "1.0",
  "producer": {
    "name": "app_name_{{ ENVIRONMENT }}_producer",
    "kms_key_alias": "alias/app_name_{{ ENVIRONMENT }}_producer_key",
    "shards": 1,
    "subscriptions": [
      {
        "name": "app_name1_{{ ENVIRONMENT }}_consumer",
        "account_id": "123456789012",
        "active": true
      },
      {
        "name": "app_name2_{{ ENVIRONMENT }}_consumer",
        "account_id": "987654321098",
        "active": true
      }
    ]
  },
  "consumers": [
    {
      "name": "app_name_{{ ENVIRONMENT }}_consumer",
      "kms_key_alias": "alias/app_name_{{ ENVIRONMENT }}_consumer_key",
      "shards": 1
    }
  ],
  "overrides": {
    "prod": {
      "producer": {
        "shards": 2,
        "subscriptions": [
          {
            "name": "app_name1_{{ ENVIRONMENT }}_consumer",
            "account_id": "123456789012",
            "active": false
          }
        ]
      },
      "consumers": [
        {
          "name": "app_name_{{ ENVIRONMENT }}_consumer",
          "shards": 3
        }
      ]
    }
  }
}

我的目标是为该环境生成最终配置。环境是一个变量。

的配置prod会有所不同。例如producer.shards在 prod 中将是 2,而不是 1。

过程是,读取 json 文件,其中的一部分producerconsumers保存到一个新的变量new_json中。这可以理解为默认值。

{
  "producer": {
     ...
  }
  consumers": {
    ...
  }
} 

然后读取覆盖的会话,如果环境是prod,它将覆盖相关键中的值。

我编写了forEach读取每个键的代码overrides,然后我需要确认键是列表或映射,然后转到该部分的末尾,替换 中的相同键new_json

const json = JSON.parse(template)

if ({}.hasOwnProperty.call(overrides, environment)) {
  const env = overrides[environment];

  Object.keys(env).forEach((key){
      ...
  }
}

示例 json 是一个简单的示例。真正的 json 有几百行。所以遍历每个键不是有效的方法。

是否有任何快速/智能的方法可以直接覆盖覆盖中的任何键new_json

更新#1

使用Object.assign(),我仍然需要遍历每个键,否则,我得到的键更少。

例如,使用下面的代码,我在subscriptions.

$ cat a.js
const fs = require('fs');
const swig = require('swig-templates');

const environment='prod';

let template = swig.compileFile("jsoncontent.json");

template = template({
  ENVIRONMENT: environment,
});

const json = JSON.parse(template);

Object.assign(json.producer, json.overrides.prod.producer)
delete json.overrides

console.log(JSON.stringify(json, null, 2));

$ node a.js
{
  "version": "1.0",
  "producer": {
    "name": "app_name_prod_producer",
    "kms_key_alias": "alias/app_name_prod_producer_key",
    "shards": 2,
    "subscriptions": [
      {
        "name": "app_name1_prod_consumer",
        "account_id": "123456789012",
        "active": false
      }
    ]
  },
  "consumers": [
    {
      "name": "app_name_prod_consumer",
      "kms_key_alias": "alias/app_name_prod_consumer_key",
      "shards": 1
    }
  ]
}

更新#2

感谢我指向lodash.defaultsDeep,但是当合并列表中的地图时,它必须在同一位置一对一匹配。

使用存在作为示例,如果我稍微调整overrides一下,订阅的第一个列表,名称从更改app_name1app_name2

"overrides": {
    "prod": {
      "producer": {
        "shards": 2,
        "subscriptions": [
          {
            "name": "app_name2_{{ ENVIRONMENT }}_consumer",
            "account_id": "123456789012",
            "active": false
          }
        ]
      }
  }

结果将是:

{ shards: 2,
  subscriptions:
   [ { name: 'app_name2_prod_consumer',
       account_id: '123456789012',
       active: false },
     { name: 'app_name2_prod_consumer',
       account_id: '987654321098',
       active: true } ],
  name: 'app_name_prod_producer',
  kms_key_alias: 'alias/app_name_prod_producer_key' }

现在有两个app_name2

标签: node.js

解决方案


Object.assign似乎正是您所需要的。

例子:

var o1 = {
  a: 1,
  b: 2
}

var override = {
  a: 9
}

Object.assign(o1, override)
console.log(o1) // {a: 9, b: 2}

编辑

Object.assign 仅循环通过根级别。
对于深度分配,您可以使用lodash.defaultsDeep

var env = _.defaultsDeep(json.overrides.prod.producer, json.producer)

推荐阅读