如题所述
需求
比如想获取这样的一个json结构
{
"user_id":"user.id",//获取用户ID
"book_id":"book.id",//获取书籍ID,
}
很简单可以实现
var user = {
id: 1
}
var book = {
id: 2
}
function getJson(user_id, book_id){
return {
"user_id": user_id,
"book_id": book_id
}
}
console.log(getJson(user.id,book.id))
这样简单的结构这样来实现,没啥问题,也很直观。
假如想获取
{
"user_id":"user.id",//获取用户ID
"book_id":"book.id",//获取书籍ID,
"adress_id":"address.id"// 获取地址ID
}
代码要改下
var user = {
id: 1
}
var book = {
id: 2
}
var address = {
id: 3
}
function getJson(user_id, book_id, address_id){
return {
"user_id": user_id,
"book_id": book_id,
"address_id": address_id
}
}
console.log(getJson(user.id,book.id,address.id))
随着获取结构越来越复杂,代码会越来越臃肿。
如何简化
json 结构 的value 值,是从不同实体获取到的,那么这个value值,可以把它定义成一个结构,来表述value。
比如这样(潜套对象)
{
"user_id":":user.id",
"book_id":":book.id",
"address_id": {
"_data_source": "address",
"id": ":id"
}
}
还有这样(数组)
[ ":user.id", ":book.id", ":address.id"]
更简单的像这样
":user.id"
以冒号开头的说明是要从实体中动态获取的。
需要定义一个解析器,对上方的结构进行解析
解析器定义为
var test = {
is_array: function(value) {
if (typeof Array.isArray === 'function') {
return Array.isArray(value)
}
return Object.prototype.toString.call(value) === '[object Array]'
},
is_object: function (value) {
return Object.prototype.toString.call(value) === '[object Object]'
},
is_string: function(value) {
return typeof value === 'string'
}
}
function replaceParams(params, current_data_source) {
if (test.is_array(params)) {
params = params.map(param=>replaceParams(param, current_data_source))
}
else if (test.is_object(params)) {
if (params._data_source) {
params = replaceParams(params.key, getDataSource(params))
} else {
if (test.is_array(current_data_source)) {
params = current_data_source.map(item=> replaceParams(JSON.parse(JSON.stringify(params)), item))
} else {
Object.keys(params).map(key => params[key]=replaceParams(params[key], current_data_source))
}
}
}
else if (test.is_string(params)) {
params = replaceParam(params, current_data_source)
}
return params
}
function replaceParam(param, obj) {
if (!test.is_string(param)) {
return param
}
if (param.indexOf(':') < 0) {
return param;
}
if (param.startsWith(':')) {
console.log('replaceParam', param, '->', data_get(obj, param.slice(1)))
return data_get(obj, param.slice(1));
}
return param.replace(/:([\w\.]+)/g, function (match, key) {
console.log('replaceParam', ':'+key, '->', data_get(obj, key))
return data_get(obj, key);
});
}
还需要定义一个实体容器,里面有user、book、address, 简称为数据源
数据源定义为
var user = {
id: 1
}
var book = {
id: 2
}
var address = {
id: 3
}
var data_sources = {}
data_sources.user = user
data_sources.book = book
data_sources.address = address
function getDataSource(config) {
return data_sources[config._data_source] || data_sources
}
默认返回 data_sources
需要实现一个 data_get 方法
类似于这样 data_get(obj, 'user.id')
function data_get(target, path, fallback) {
let segments = Array.isArray(path) ? path : path.split('.');
let [segment] = segments;
let find = target;
if (segment !== '*' && segments.length > 0) {
if (find[segment] === null || typeof find[segment] === 'undefined') {
find = typeof fallback === 'function' ? fallback() : fallback;
}
else {
find = data_get(find[segment], segments.slice(1), fallback);
}
}
else if (segment === '*') {
const partial = segments.slice(path.indexOf('*') + 1, path.length);
if (typeof find === 'object') {
find = Object.keys(find).reduce((build, property) => ({
...build,
[property]: data_get(find[property], partial, fallback)
}),
{});
}
else {
find = data_get(find, partial, fallback);
}
}
/*-----------------------------------------------------------------------------
| Arrayable Requirements
*-----------------------------------------------------------------------------
|
| . All arrays are converted to objects
| . For Example
| #Code
| Code -> data_set({ list: ['one', 'two', 'three'], 'list.*', 'update', true });
|
| #Input
| Input -> { list: ['one', 'two', 'three'] }
|
| #During We Convert Arrays To "Indexed Objects"
| During -> { list: { '1': 'one', '2': 'two', '3': 'three' } }
|
| #Before Output we convert "Indexed Objects" Back To Arrays
| From -> { list: { '1': 'update', '2': 'update', '3': 'update' } }
| Into -> { list: ['update', 'update', 'update'] }
|
| . Arrays convert into "Indexed Objects", allowing for wildcard (*) capabilities
| . "Indexed Objects" are converted back into arrays before returning the updated target
|
*/
if (typeof find === 'object') {
if (Object.prototype.toString.call(find) === '[object Array]') {
if (find.length == 0) {
return find
}
}
if (Object.keys(find).length > 0) {
const isArrayTransformable = Object.keys(find).every(index => index.match(/^(0|[1-9][0-9]*)$/));
return isArrayTransformable ? Object.values(find) : find;
}
} else {
return find;
}
};
这样就定义完成了,所有代码为
var user = {
id: 1
}
var book = {
id: 2
}
var address = {
id: 3
}
var data_sources = {}
data_sources.user = user
data_sources.book = book
data_sources.address = address
function getDataSource(config) {
return data_sources[config._data_source] || data_sources
}
var test = {
is_array: function(value) {
if (typeof Array.isArray === 'function') {
return Array.isArray(value)
}
return Object.prototype.toString.call(value) === '[object Array]'
},
is_object: function (value) {
return Object.prototype.toString.call(value) === '[object Object]'
},
is_string: function(value) {
return typeof value === 'string'
}
}
function replaceParams(params, current_data_source) {
if (test.is_array(params)) {
params = params.map(param=>replaceParams(param, current_data_source))
}
else if (test.is_object(params)) {
if (params._data_source) {
params = replaceParams(params.key, getDataSource(params))
} else {
if (test.is_array(current_data_source)) {
params = current_data_source.map(item=> replaceParams(JSON.parse(JSON.stringify(params)), item))
} else {
Object.keys(params).map(key => params[key]=replaceParams(params[key], current_data_source))
}
}
}
else if (test.is_string(params)) {
params = replaceParam(params, current_data_source)
}
return params
}
function replaceParam(param, obj) {
if (!test.is_string(param)) {
return param
}
if (param.indexOf(':') < 0) {
return param;
}
if (param.startsWith(':')) {
console.log('replaceParam', param, '->', data_get(obj, param.slice(1)))
return data_get(obj, param.slice(1));
}
return param.replace(/:([\w\.]+)/g, function (match, key) {
console.log('replaceParam', ':'+key, '->', data_get(obj, key))
return data_get(obj, key);
});
}
function data_get(target, path, fallback) {
let segments = Array.isArray(path) ? path : path.split('.');
let [segment] = segments;
let find = target;
if (segment !== '*' && segments.length > 0) {
if (find[segment] === null || typeof find[segment] === 'undefined') {
find = typeof fallback === 'function' ? fallback() : fallback;
}
else {
find = data_get(find[segment], segments.slice(1), fallback);
}
}
else if (segment === '*') {
const partial = segments.slice(path.indexOf('*') + 1, path.length);
if (typeof find === 'object') {
find = Object.keys(find).reduce((build, property) => ({
...build,
[property]: data_get(find[property], partial, fallback)
}),
{});
}
else {
find = data_get(find, partial, fallback);
}
}
/*-----------------------------------------------------------------------------
| Arrayable Requirements
*-----------------------------------------------------------------------------
|
| . All arrays are converted to objects
| . For Example
| #Code
| Code -> data_set({ list: ['one', 'two', 'three'], 'list.*', 'update', true });
|
| #Input
| Input -> { list: ['one', 'two', 'three'] }
|
| #During We Convert Arrays To "Indexed Objects"
| During -> { list: { '1': 'one', '2': 'two', '3': 'three' } }
|
| #Before Output we convert "Indexed Objects" Back To Arrays
| From -> { list: { '1': 'update', '2': 'update', '3': 'update' } }
| Into -> { list: ['update', 'update', 'update'] }
|
| . Arrays convert into "Indexed Objects", allowing for wildcard (*) capabilities
| . "Indexed Objects" are converted back into arrays before returning the updated target
|
*/
if (typeof find === 'object') {
if (Object.prototype.toString.call(find) === '[object Array]') {
if (find.length == 0) {
return find
}
}
if (Object.keys(find).length > 0) {
const isArrayTransformable = Object.keys(find).every(index => index.match(/^(0|[1-9][0-9]*)$/));
return isArrayTransformable ? Object.values(find) : find;
}
} else {
return find;
}
};
验证
console.log(replaceParams({
"user_id":":user.id",
"book_id":":book.id",
"address_id": {
"_data_source": "address",
"key": ":id"
}
}, data_sources))
//output {user_id:1,book_id:2,address_id:3}
console.log(replaceParams([
":user.id",
":book.id",
":address.id"
], data_sources))
//output [1,2, 3]
console.log(replaceParams([
":user.id",
":book.id",
{
"_data_source": "address",
"key": ":id"
}
], data_sources))
//output [1, 2, 3]
console.log(replaceParams([
":user.id",
":book.id",
[
":user.id",
":book.id",
]
], data_sources))
//output [1, 2, [1,2]]
console.log(replaceParams([
":user.id",
":book.id",
[
":user.id",
":book.id",
":address"
]
], data_sources))
//output [1, 2, [1,2,{id:3}]]
高级用法
data_sources.users = [
{
id:1,
name:"11"
},
{
id:2,
name:"22"
}
]
data_sources.post = {
id:4,
comment:{
id:5
}
}
console.log(JSON.stringify(replaceParams({
'test': [
":user.id",
":book.id",
[
":user.id",
":book.id",
":address"
]
],
"users": ":users",
"_map_users": {
"_data_source":"users",
"key": {
"name": ":name"
}
},
"user_ids": ":users.*.id",
"extra": {
"user_ids": ":users.*.id",
"_user_ids": {
"_data_source":"users",
"key":":*.id"
},
"post": {
"_data_source":"post",
"key": {
"post_id": ":id",
"comment": ":comment",
"comment_id":":comment.id",
"_user_ids": {
"_data_source":"users",
"key":":*.id"
}
}
}
}
}, data_sources)))
// output {"test":[1,2,[1,2,{"id":3}]],"users":[{"id":1,"name":"11"},{"id":2,"name":"22"}],"_map_users":[{"name":"11"},{"name":"22"}],"user_ids":[1,2],"extra":{"user_ids":[1,2],"_user_ids":[1,2],"post":{"post_id":4,"comment":{"id":5},"comment_id":5,"_user_ids":[1,2]}}}
更进一步
-
当数据源是 callback 时
-
当callback 返回的是promise 时
-
当这个思路应用到laravel的项目中时,比如api返回的结构
ref
- data_get->github.com/zhorton34/l…