假设我有以下JSON对象,
dbConfig = {
"db": "default",
"default": {
"defaultDB": "sqlite",
"init": "init",
"migrations": {
"directory": "migrations",
"tableName": "migrations"
},
"pool": {
"min": "2",
"max": "10"
},
"sqlite": {
"client": "sqlite3",
"connection": {
"filename": "data/default/sqlitedb/test.db"
}
},
"oracle": {
"client": "oracledb",
"config": {
"development": {
"user": "test",
"pass": "test",
"db": "test"
},
"production": {
"user": "test",
"pass": "test",
"db": "test"
},
"test": {
"user": "test",
"pass": "test",
"db": "test"
}
}
}
}
};
使用节点&Lodash,根据dbConfig.default[dbConfig.default.defaultDB]
的设置,是否有可能获得connection
或config.
?
例如,如果我设置dbConfig.default.defaultDB=oracledb
和process.env.NODE_ENV=development
,我希望能够获得dbConfig.default[dbConfig.default.defaultDB].config.development
或者如果我设置dbConfig.default.defaultDB=sqlite
只是为了获得dbConfig.default[dbConfig.default.defaultDB].connection
换句话说,如果数据库具有特定于环境的配置,则该配置将在"config": {}
中,如果不在"connection": {}
中
不一定是洛达什。它也可以是纯javascript。
不含碘的溶液
var defaultDbName = dbConfig.default[dbConfig.default.defaultDB];
var db;
if (defaultDb === 'sqllite') {
db = dbConfig.default[defaultDb].connection;
} else {
var env = process.env.NODE_ENV;
db = dbConfig.default[defaultDb].config[env];
}
含有碘的溶液
在这里,我使用lodash get
函数来获取对象字段值,如果不存在,则使用null
。此外,我使用模板字符串语法:${val}
来格式化字段路径。
var defaultDbName = dbConfig.default[dbConfig.default.defaultDB];
var defaultDbConf = dbConfig.default[defaultDb];
var env = process.env.NODE_ENV;
var db = defaultDbConf.connection || _.get(defaultDbConf, `config.${env}`);
顺便说一句,您的配置json太复杂了,最好按环境进行配置。
没有[依赖项]的解决方案(最初在此处回答,但不是AngularJS特定的)
是的,您的JSON很复杂,但在没有所有重复的情况下,它也可以更小、更可读,因为每个环境都有相同的一组属性,这些属性可能会变化,也可能不会变化,并且会被不必要地重复。
使用一个简单的算法(jsFiddle),您可以动态解析JSON配置中的特定属性名称后缀(property@suffix),并拥有环境变化属性和非变化属性的目录,而无需人为构建配置,也无需重复,包括深度嵌套的配置对象。
您还可以混合和匹配后缀,并结合任何数量的环境或其他任意因素来完善您的配置对象。
示例,预处理JSON配置的片段:
var config = {
'help': {
'BLURB': 'This pre-production environment is not supported. Contact Development Team with questions.',
'PHONE': '808-867-5309',
'EMAIL': 'coder.jen@lostnumber.com'
},
'help@www.productionwebsite.com': {
'BLURB': 'Please contact Customer Service Center',
'BLURB@fr': 'S'il vous plaît communiquer avec notre Centre de service à la clientèle',
'BLURB@de': 'Bitte kontaktieren Sie unseren Kundendienst!!1!',
'PHONE': '1-800-CUS-TOMR',
'EMAIL': 'customer.service@productionwebsite.com'
},
}
并进行后处理(给定位置。hostname="www.productionwebsite.com",navigator.language为"de'):
prefer(config,['www.productionwebsite.com','de']); // prefer(obj,string|Array<string>)
JSON.stringify(config); // {
'help': {
'BLURB': 'Bitte kontaktieren Sie unseren Kundendienst!!1!',
'PHONE': '1-800-CUS-TOMR',
'EMAIL': 'customer.service@productionwebsite.com'
}
}
显然,你可以在渲染时使用location.hostname和window.navigator.langage提取这些值。处理JSON的算法本身并不复杂(但出于某种原因,你可能仍然对整个框架而不是单个函数感到更舒服):
function prefer(obj,suf) {
function pr(o,s) {
for (var p in o) {
if (!o.hasOwnProperty(p) || !p.split('@')[1] || p.split('@@')[1] ) continue; // ignore: proto-prop OR not-suffixed OR temp prop score
var b = p.split('@')[0]; // base prop name
if(!!!o['@@'+b]) o['@@'+b] = 0; // +score placeholder
var ps = p.split('@')[1].split('&'); // array of property suffixes
var sc = 0; var v = 0; // reset (running)score and value
while(ps.length) {
// suffix value: index(of found suffix in prefs)^10
v = Math.floor(Math.pow(10,s.indexOf(ps.pop())));
if(!v) { sc = 0; break; } // found suf NOT in prefs, zero score (delete later)
sc += v;
}
if(sc > o['@@'+b]) { o['@@'+b] = sc; o[b] = o[p]; } // hi-score! promote to base prop
delete o[p];
}
for (var p in o) if(p.split('@@')[1]) delete o[p]; // remove scores
for (var p in o) if(typeof o[p] === 'object') pr(o[p],s); // recurse surviving objs
}
if( typeof obj !== 'object' ) return; // validate
suf = ( (suf || suf === 0 ) && ( suf.length || suf === parseFloat(suf) ) ? suf.toString().split(',') : []); // array|string|number|comma-separated-string -> array-of-strings
pr(obj,suf.reverse());
}
属性名称后缀在"@"之后可以有任意数量的后缀,由"&"分隔(与号)和,如果有两个具有不同但首选后缀的属性,则按照它们传递给函数的顺序,它们将是首选的。包含两个首选字符串的后缀将优先于所有其他字符串。在JSON中找到的未指定为首选的后缀将被丢弃。
偏好/区分将自上而下地应用于对象树,如果更高级别的对象仍然存在,则随后将检查它们的首选后缀。
使用这种方法,您的JSON(我假设哪些属性在您的环境中不同,哪些属性不同)可能会简化如下:
dbConfig = {
"pool": {
"min": "2",
"max": "10"
},
"init": "init",
"migrations": {
"directory": "migrations",
"tableName": "migrations"
},
"db":
"client": "sqlite",
"filename": "data/default/sqlitedb/development.db"
"filename@tst": "data/default/sqlitedb/test.db"
"filename@prd": "data/default/sqlitedb/production.db"
},
"db@oracle": {
"client": "oracle",
"user": "devuser",
"user@tst": "testdbuser",
"user@prd": "testdbuser",
"pass": "devpass",
"pass@tst": "testdbpass",
"pass@prd": "testdbpass",
"db": "devdb",
"db@tst": "testdbschema",
"db@prd": "testdbschema"
}
};
因此,您可以将其输入到prefer()函数中,其中包含以下args+结果:
对于sqlite,测试env:
prefer(dbConfig,'tst');
JSON.stringify(dbConfig); // dbConfig: {
"pool": {
"min": "2",
"max": "10"
},
"init": "init",
"migrations": {
"directory": "migrations",
"tableName": "migrations"
},
"db": {
"client": "sqlite",
"filename": "data/default/sqlitedb/test.db"
}
};
对于oracle,默认/开发环境:
prefer(dbConfig,'oracle'); // oracle, dev(default) env
JSON.stringify(dbConfig); // dbConfig: {
"pool": {
"min": "2",
"max": "10"
},
"init": "init",
"migrations": {
"directory": "migrations",
"tableName": "migrations"
},
"db": {
"client": "oracle",
"user": "devdbuser",
"pass": "devdbpass",
"db": "devdbschema"
}
};
prefer(dbConfig,'oracle,prd'); // oracle, production env
JSON.stringify(dbConfig); // dbConfig: {
"pool": {
"min": "2",
"max": "10"
},
"init": "init",
"migrations": {
"directory": "migrations",
"tableName": "migrations"
},
"db": {
"client": "oracle",
"user": "prddbuser",
"pass": "prddbpass",
"db": "prddbschema"
}
};
摘要用法和示例:
var o = { 'a':'apple', 'a@dev':'apple-dev', 'a@fr':'pomme',
'b':'banana', 'b@fr':'banane', 'b@dev&fr':'banane-dev',
'c':{ 'o':'c-dot-oh', 'o@fr':'c-point-oh' }, 'c@dev': { 'o':'c-dot-oh-dev', 'o@fr':'c-point-oh-dev' } };
/*1*/ prefer(o,'dev'); // { a:'apple-dev', b:'banana', c:{o:'c-dot-oh-dev'} }
/*2*/ prefer(o,'fr'); // { a:'pomme', b:'banane', c:{o:'c-point-oh'} }
/*3*/ prefer(o,'dev,fr'); // { a:'apple-dev', b:'banane-dev', c:{o:'c-point-oh-dev'} }
/*4*/ prefer(o,['fr','dev']); // { a:'pomme', b:'banane-dev', c:{o:'c-point-oh-dev'} }
/*5*/ prefer(o); // { a:'apple', b:'banana', c:{o:'c-dot-oh'} }
注意事项@in属性名称的使用不是标准的,在点表示法中也是无效的,但到目前为止,还没有破坏我们在其中测试过的任何浏览器。这样做的好处是,它防止了开发人员期望他们可以引用您预处理的带后缀的属性。开发人员必须意识到这一点,并且有点非常规,并将您的属性称为字符串(obj[]key@suf’])这样做,顺便说一句,这就是这个功能成为可能的原因。
如果未来的JavaScript引擎拒绝它,用任何其他可以容忍的约定来代替,只要保持一致即可。该算法尚未进行性能分析,也未针对其他潜在问题进行严格测试。在目前的形式中,在启动/加载时一次性使用,我们还没有遇到问题。一如既往,YMMV。