如何根据不同的客户端更改json对象的键 [英] How to change keys of json object based on different client
问题描述
我有一个需求,其中 API 响应密钥将根据客户端而变化,例如,如果我有两个客户端,客户端 1 和客户端 2,并且我的原始 json 数据如下
<预><代码>{目的地地址":[美国华盛顿特区",美国宾夕法尼亚州费城",美国加利福尼亚州圣巴巴拉",美国佛罗里达州迈阿密",美国德克萨斯州奥斯汀",美国加利福尼亚州纳帕县"],来源地址":[纽约,纽约,美国"],行":[{元素":[{距离":{文本":227 英里",值":365468},持续时间":{文本":3 小时 54 分钟",值":14064},状态":OK"},{距离":{文本":94.6 英里",值":152193},持续时间":{文本":1 小时 44 分钟",值":6227},状态":OK"},{距离":{文本":2,878 英里",价值":4632197},持续时间":{文本":1 天 18 小时",值":151772},状态":OK"},{距离":{文本":1,286 英里",价值":2069031},持续时间":{文本":18 小时 43 分钟",值":67405},状态":OK"},{距离":{文本":1,742 英里",值":2802972},持续时间":{文本":1 天 2 小时",值":93070},状态":OK"},{距离":{文本":2,871 英里",价值":4620514},持续时间":{文本":1 天 18 小时",值":152913},状态":OK"}]}],状态":OK"}但是对于客户端 1,密钥应该不同,例如 destination_addresses 将是 dest_address,而对于客户端 2,密钥将是 destaddress,并且可以进行此密钥更改,直到子级别,例如客户端 1 和客户端的距离密钥将是 dist2 对于客户 1 来说,这将是别的东西例如,客户端 1 的上述响应如下
<预><代码>{dest_address":[美国华盛顿特区",美国宾夕法尼亚州费城",美国加利福尼亚州圣巴巴拉",美国佛罗里达州迈阿密",美国德克萨斯州奥斯汀",美国加利福尼亚州纳帕县"],og_addresses":[纽约,纽约,美国"],第 1 行":[{元素客户端":[{分布":{t":227 英里",v":365468},持续时间":{t":3 小时 54 分钟",值":14064},状态":OK"},{分布":{t":94.6 英里",值":152193},持续时间":{t":1 小时 44 分钟",值":6227},状态":OK"},{分布":{t":2,878 英里",价值":4632197},持续时间":{t":1 天 18 小时",值":151772},状态":OK"},{分布":{t":1,286 英里",价值":2069031},持续时间":{t":18 小时 43 分钟",值":67405},状态":OK"},{分布":{t":1,742 英里",值":2802972},持续时间":{t":1 天 2 小时",值":93070},状态":OK"},{分布":{t":2,871 英里",价值":4620514},持续时间":{t":1 天 18 小时",值":152913},状态":OK"}]}],状态":OK"}并且它可以是客户端 2 的不同键,所以我如何实现这一点,因为对象可以嵌套并且客户端可以很多,我尝试了很多像递归这样的事情,但都无法做到这一点及其特定于客户的那是您最后在这里发布我的查询,下面是我正在编写的代码,因为您可以看到父键我可以更改它但嵌套的我不知道我该怎么做
const 键 = {destination_addresses":dest_address",origin_addresses":org_address"}const newObj = {};递归迭代属性(对象)函数 recursivelyIterateProperties(jsonObject,key) {如果(键){如果(键[键]){newObj[keys[key]] = jsonObject}}console.log(jsonObject,'<----->>>>>',key)if (jsonObject instanceof Array) {const d = jsonObject[0];if (typeof(d) === '对象'){const obj = Object.keys(jsonObject)for (var prop in obj) {if (!(typeof(jsonObject[obj[prop]]) === 'string')) {recursivelyIterateProperties(jsonObject[obj[prop]],obj[prop]);}}}//for (var i = 0; i < jsonObject.length; ++i) {//递归迭代Properties(jsonObject[i],key)//}}否则 if (typeof(jsonObject) === 'object') {const obj = Object.keys(jsonObject)for (var prop in obj) {if (!(typeof(jsonObject[obj[prop]]) === 'string')) {recursivelyIterateProperties(jsonObject[obj[prop]],obj[prop]);}别的{}}}}控制台日志(新对象);
这是一个可能的答案.我对它并不感到兴奋,但现在没有更多的时间来处理它.
想法是使用这样的配置对象:
{'destination_addresses|#': 'dest_addresses|#','origin_addresses|#': 'og_addresses|#','行|#|元素|#|距离|文本':'行|#|元素|#|距离|t','行|#|元素|#|距离|值':'行|#|元素|#|距离|v','行|#|元素|#|持续时间|文本':'行|#|元素|#|持续时间|t','行|#|元素|#|持续时间|值':'行|#|元素|#|持续时间|v',}
以及将输入对象扁平化为键值对的代码,其中键记录对象结构,如下所示:
<预><代码>[[destination_addresses|0",美国华盛顿特区"][destination_addresses|1",美国宾夕法尼亚州费城"]//...[origin_addresses|0",美国纽约州纽约市"][行|0|元素|0|距离|文本",227英里"][行|0|元素|0|距离|值",365468"][行|0|元素|0|持续时间|文本",3 小时 54 分钟"][行|0|元素|0|持续时间|值",14064"][行|0|元素|0|状态",确定"][行|0|元素|1|距离|文本",94.6 英里"][行|0|元素|1|距离|值",152193"]//...[行|0|元素|5|持续时间|文本",1 天 18 小时"][行|0|元素|5|持续时间|值",152913"][行|0|元素|5|状态",确定"][状态",确定"]]并将其转换为
[[dest_addresses|0",美国华盛顿特区"][dest_addresses|1",美国宾夕法尼亚州费城"]//...[og_addresses|0",美国纽约州纽约市"][行|0|元素|0|距离|t",227 英里"][行|0|元素|0|距离|v",365468"][行|0|元素|0|持续时间|t",3 小时 54 分钟"][行|0|元素|0|持续时间|v",14064"][行|0|元素|0|状态",确定"][行|0|元素|1|距离|t",94.6 英里"][行|0|元素|1|距离|v",152193"]//..[行|0|元素|5|持续时间|t",1 天 18 小时"][行|0|元素|5|持续时间|v",152913"][行|0|元素|5|状态",确定"][状态",确定"]]
然后最后将其重新水化成一个实际的物体.
以下代码段中的代码执行此操作.它使用了一些从 Ramda 借来的 API 函数(免责声明:我是作者之一),尽管这里的实现是不同的.并且它使用了许多通用的实用函数,例如 getPaths
、pathEntries
、hydrate
和 reconstruct
,以及然后是一些辅助函数,所有这些都用于使主函数 convertStructure
变得相当简单.
//从 Ramda 借来的实用函数const path = (ps = []) =>(obj = {}) =>ps .reduce ((o, p) => (o || {}) [p], obj)const assoc = (prop, val, obj) =>数字 .isInteger (prop) &&数组 .isArray (obj)?[... obj .slice (0, prop), val, ...obj .slice (prop + 1)]: {...obj, [prop]: val}const assocPath = ([p = undefined, ...ps], val, obj) =>p == 未定义?对象: ps.length == 0?关联(p,val,obj): assoc(p, assocPath(ps, val, obj[p] || (obj[p] = Number .isInteger (ps[0]) ? [] : {})), obj)//通用实用函数const getPaths = (obj) =>对象 (obj) === obj?对象 .entries (obj) .flatMap (([k, v]) =>getPaths (v) .map (p => k + (p ? '|' : '') + p)): ['']const pathEntries = (obj) =>getPaths (obj) .map (p => [p, path (p.split('|')) (obj)])const fixPath = (p) =>p .split ('|') .map (n =>/^\d+$/.test(n) ? Number(n) : n)const 水合物 = (条目) =>条目 .reduce ((a, [k, v]) => assocPath (fixPath (k), v, a), {})const 重构 = (fn) =>(对象) =>水合物(fn(pathEntries(obj)))//辅助函数const addHash = (config) =>(键) =>key .split ('|') .map (k =>/^\d+$/.test (k) ? '#' : k) .join ('|')const convertKey = (config) =>(key, hashed = addHash (config) (key)) =>在配置中散列?配置 [散列] : 散列const reconstituteKey = (template, key, parts = key .split ('|')) =>模板 .split ('|') .map ((k, i) => k == '#' ?parts [i] : k) .join ('|')//主功能const convertStructure = (config) =>重建 (pathEntries => pathEntries .map (([k, v]) =>[reconstituteKey (convertKey (config) (k), k), v]))//样本输入const input = {destination_addresses: [华盛顿特区,美国",费城,宾夕法尼亚州,美国",圣巴巴拉,加利福尼亚州,美国",迈阿密,佛罗里达州,美国",德克萨斯州奥斯汀,美国",纳帕County, CA, USA"], origin_addresses: ["New York, NY, USA"], rows: [{elements: [{distance: {text: "227 mi", value: 365468}, duration: {text: "3 hours 54 mins", value: 14064}, status: "OK"}, {distance: {text: "94.6 mi", value: 152193}, duration: {text: "1 hours 44 mins", value: 6227}, status: "OK"}, {distance: {text: "2,878 mi", value: 4632197}, duration: {text: "1 day 18 hours", value: 151772}, status: "OK"}, {distance: {text: "1,286 mi", value: 2069031}, duration: {text: "18 hours 43 mins", value: 67405}, status: "OK"}, {distance: {text: "1,742 mi", value: 2802972}, 持续时间: {text: "1 day 2 hours", value: 93070}, status: "OK"}, {distance: {text: "2,871 mi", value: 4620514}, duration: {text: "1 天 18 小时",值:152913},状态:OK"}]}],状态:OK"}常量配置 = {'destination_addresses|#': 'dest_addresses|#','origin_addresses|#': 'og_addresses|#','行|#|元素|#|距离|文本':'行|#|元素|#|距离|t','行|#|元素|#|距离|值':'行|#|元素|#|距离|v','行|#|元素|#|持续时间|文本':'行|#|元素|#|持续时间|t','行|#|元素|#|持续时间|值':'行|#|元素|#|持续时间|v',}//演示控制台 .log (convertStructure (config) (input))
.as-console-wrapper {max-height: 100% !important;顶部:0}
此解决方案有几个缺点.首先,通过使用字符串键,我们限制了我们可以处理的元素的属性名称.通常我会选择使用 ['rows', 0, 'elements', 1, 'distance', 'text']
而不是 'rows|0|elements|1|distance|文本'
.有很多很好的理由,但这种格式使得配置不太方便.但请注意,如果您的属性名称包含 '|'
或 '#'
,则必须更改其中的某些代码.
其次,此解决方案要求您重命名 leaf 路径.如果我们想将 'rows|#|elements|#|duration'
重命名为 'rows|#|elements|#|time'
,我们必须同时处理text
属性和 value
属性,即使我们没有改变它们.我很想看到一个不需要这个的变体,但我没有一个好的选择.
无论如何,我希望它能满足您的需求.还有更多,我希望其他人可以提供更好的解决方案.
i have a requirement where the API response key will be changing depending upon the client for example if I have two client, client 1 and client 2 and my original json data is like below
{
"destination_addresses": [
"Washington, DC, USA",
"Philadelphia, PA, USA",
"Santa Barbara, CA, USA",
"Miami, FL, USA",
"Austin, TX, USA",
"Napa County, CA, USA"
],
"origin_addresses": [
"New York, NY, USA"
],
"rows": [{
"elements": [{
"distance": {
"text": "227 mi",
"value": 365468
},
"duration": {
"text": "3 hours 54 mins",
"value": 14064
},
"status": "OK"
},
{
"distance": {
"text": "94.6 mi",
"value": 152193
},
"duration": {
"text": "1 hour 44 mins",
"value": 6227
},
"status": "OK"
},
{
"distance": {
"text": "2,878 mi",
"value": 4632197
},
"duration": {
"text": "1 day 18 hours",
"value": 151772
},
"status": "OK"
},
{
"distance": {
"text": "1,286 mi",
"value": 2069031
},
"duration": {
"text": "18 hours 43 mins",
"value": 67405
},
"status": "OK"
},
{
"distance": {
"text": "1,742 mi",
"value": 2802972
},
"duration": {
"text": "1 day 2 hours",
"value": 93070
},
"status": "OK"
},
{
"distance": {
"text": "2,871 mi",
"value": 4620514
},
"duration": {
"text": "1 day 18 hours",
"value": 152913
},
"status": "OK"
}
]
}],
"status": "OK"
}
But for client 1 the key should be different like destination_addresses will be dest_address and for client 2 the key will be destaddress and this key change can be done till the child level like the distance key will be dist for client 1 and for client 2 it will be something else so for client 1 For ex the above response for client 1 would be like below
{
"dest_address": [
"Washington, DC, USA",
"Philadelphia, PA, USA",
"Santa Barbara, CA, USA",
"Miami, FL, USA",
"Austin, TX, USA",
"Napa County, CA, USA"
],
"og_addresses": [
"New York, NY, USA"
],
"row1": [{
"elements_client": [{
"dist": {
"t": "227 mi",
"v": 365468
},
"duration": {
"t": "3 hours 54 mins",
"value": 14064
},
"status": "OK"
},
{
"dist": {
"t": "94.6 mi",
"value": 152193
},
"duration": {
"t": "1 hour 44 mins",
"value": 6227
},
"status": "OK"
},
{
"dist": {
"t": "2,878 mi",
"value": 4632197
},
"duration": {
"t": "1 day 18 hours",
"value": 151772
},
"status": "OK"
},
{
"dist": {
"t": "1,286 mi",
"value": 2069031
},
"duration": {
"t": "18 hours 43 mins",
"value": 67405
},
"status": "OK"
},
{
"dist": {
"t": "1,742 mi",
"value": 2802972
},
"duration": {
"t": "1 day 2 hours",
"value": 93070
},
"status": "OK"
},
{
"dist": {
"t": "2,871 mi",
"value": 4620514
},
"duration": {
"t": "1 day 18 hours",
"value": 152913
},
"status": "OK"
}
]
}],
"status": "OK"
}
and it can be different keys for client 2, so how can I achieve this as the object can be nested and the client can be many, I tried a lot of thing like recursion and all but not able to do it and its client specific that's y at last posting my query here, below is the code that I am writing as u can see the parent key I can changes it but the nested i don't know how can I do that
const keys = {
"destination_addresses":"dest_address",
"origin_addresses" : "org_address"
}
const newObj = {};
recursivelyIterateProperties(obj)
function recursivelyIterateProperties(jsonObject,key) {
if(key){
if(keys[key]){
newObj[keys[key]] = jsonObject
}
}
console.log(jsonObject,'<----->>>>>',key)
if (jsonObject instanceof Array) {
const d = jsonObject[0];
if (typeof(d) === 'object'){
const obj = Object.keys(jsonObject)
for (var prop in obj) {
if (!(typeof(jsonObject[obj[prop]]) === 'string')) {
recursivelyIterateProperties(jsonObject[obj[prop]],obj[prop]);
}
}
}
// for (var i = 0; i < jsonObject.length; ++i) {
// recursivelyIterateProperties(jsonObject[i],key)
// }
}
else if (typeof(jsonObject) === 'object') {
const obj = Object.keys(jsonObject)
for (var prop in obj) {
if (!(typeof(jsonObject[obj[prop]]) === 'string')) {
recursivelyIterateProperties(jsonObject[obj[prop]],obj[prop]);
}else{
}
}
}
}
console.log(newObj);
Here is a potential answer. I'm not thrilled with it, but have no more time to work on it now.
The idea is to use a configuration object like this:
{
'destination_addresses|#': 'dest_addresses|#',
'origin_addresses|#': 'og_addresses|#',
'rows|#|elements|#|distance|text': 'rows|#|elements|#|distance|t',
'rows|#|elements|#|distance|value': 'rows|#|elements|#|distance|v',
'rows|#|elements|#|duration|text': 'rows|#|elements|#|duration|t',
'rows|#|elements|#|duration|value': 'rows|#|elements|#|duration|v',
}
and code which flattens your input object into key-value pairs, where the keys record the object structure, something like this:
[
["destination_addresses|0", "Washington, DC, USA"]
["destination_addresses|1", "Philadelphia, PA, USA"]
// ...
["origin_addresses|0", "New York, NY, USA"]
["rows|0|elements|0|distance|text", "227 mi"]
["rows|0|elements|0|distance|value", "365468"]
["rows|0|elements|0|duration|text", "3 hours 54 mins"]
["rows|0|elements|0|duration|value", "14064"]
["rows|0|elements|0|status", "OK"]
["rows|0|elements|1|distance|text", "94.6 mi"]
["rows|0|elements|1|distance|value", "152193"]
// ...
["rows|0|elements|5|duration|text", "1 day 18 hours"]
["rows|0|elements|5|duration|value", "152913"]
["rows|0|elements|5|status", "OK"]
["status", "OK"]
]
And converting this to
[
["dest_addresses|0", "Washington, DC, USA"]
["dest_addresses|1", "Philadelphia, PA, USA"]
// ...
["og_addresses|0", "New York, NY, USA"]
["rows|0|elements|0|distance|t", "227 mi"]
["rows|0|elements|0|distance|v", "365468"]
["rows|0|elements|0|duration|t", "3 hours 54 mins"]
["rows|0|elements|0|duration|v", "14064"]
["rows|0|elements|0|status", "OK"]
["rows|0|elements|1|distance|t", "94.6 mi"]
["rows|0|elements|1|distance|v", "152193"]
// ..
["rows|0|elements|5|duration|t", "1 day 18 hours"]
["rows|0|elements|5|duration|v", "152913"]
["rows|0|elements|5|status", "OK"]
["status", "OK"]
]
And then finally rehydrating that back into an actual object.
The code in the following snippet does this. It uses some functions with APIs borrowed from Ramda (disclaimer: I'm one of the authors), although the implementations here are different. And it uses a number of generic utility functions, such as getPaths
, pathEntries
, hydrate
, and reconstruct
, and then a few helper functions, all of which serve to make the main function, convertStructure
, fairly simple.
// Utility functions borrowed from Ramda
const path = (ps = []) => (obj = {}) =>
ps .reduce ((o, p) => (o || {}) [p], obj)
const assoc = (prop, val, obj) =>
Number .isInteger (prop) && Array .isArray (obj)
? [... obj .slice (0, prop), val, ...obj .slice (prop + 1)]
: {...obj, [prop]: val}
const assocPath = ([p = undefined, ...ps], val, obj) =>
p == undefined
? obj
: ps.length == 0
? assoc(p, val, obj)
: assoc(p, assocPath(ps, val, obj[p] || (obj[p] = Number .isInteger (ps[0]) ? [] : {})), obj)
// Generic utility functions
const getPaths = (obj) =>
Object (obj) === obj
? Object .entries (obj) .flatMap (
([k, v]) => getPaths (v) .map (p => k + (p ? '|' : '') + p)
)
: ['']
const pathEntries = (obj) =>
getPaths (obj) .map (p => [p, path (p.split('|')) (obj)])
const fixPath = (p) =>
p .split ('|') .map (n => /^\d+$/.test(n) ? Number(n) : n)
const hydrate = (entries) =>
entries .reduce ((a, [k, v]) => assocPath (fixPath (k), v, a), {})
const reconstruct = (fn) => (obj) =>
hydrate (fn (pathEntries (obj)))
// Helper functions
const addHash = (config) => (key) =>
key .split ('|') .map (k => /^\d+$/ .test (k) ? '#' : k) .join ('|')
const convertKey = (config) => (key, hashed = addHash (config) (key)) =>
hashed in config ? config [hashed] : hashed
const reconstituteKey = (template, key, parts = key .split ('|')) =>
template .split ('|') .map ((k, i) => k == '#' ? parts [i] : k) .join ('|')
// Main function
const convertStructure = (config) =>
reconstruct (pathEntries => pathEntries .map (
([k, v]) => [reconstituteKey (convertKey (config) (k), k), v])
)
// Sample input
const input = {destination_addresses: ["Washington, DC, USA", "Philadelphia, PA, USA", "Santa Barbara, CA, USA", "Miami, FL, USA", "Austin, TX, USA", "Napa County, CA, USA"], origin_addresses: ["New York, NY, USA"], rows: [{elements: [{distance: {text: "227 mi", value: 365468}, duration: {text: "3 hours 54 mins", value: 14064}, status: "OK"}, {distance: {text: "94.6 mi", value: 152193}, duration: {text: "1 hour 44 mins", value: 6227}, status: "OK"}, {distance: {text: "2,878 mi", value: 4632197}, duration: {text: "1 day 18 hours", value: 151772}, status: "OK"}, {distance: {text: "1,286 mi", value: 2069031}, duration: {text: "18 hours 43 mins", value: 67405}, status: "OK"}, {distance: {text: "1,742 mi", value: 2802972}, duration: {text: "1 day 2 hours", value: 93070}, status: "OK"}, {distance: {text: "2,871 mi", value: 4620514}, duration: {text: "1 day 18 hours", value: 152913}, status: "OK"}]}], status: "OK"}
const config = {
'destination_addresses|#': 'dest_addresses|#',
'origin_addresses|#': 'og_addresses|#',
'rows|#|elements|#|distance|text': 'rows|#|elements|#|distance|t',
'rows|#|elements|#|distance|value': 'rows|#|elements|#|distance|v',
'rows|#|elements|#|duration|text': 'rows|#|elements|#|duration|t',
'rows|#|elements|#|duration|value': 'rows|#|elements|#|duration|v',
}
// Demo
console .log (convertStructure (config) (input))
.as-console-wrapper {max-height: 100% !important; top: 0}
There are several downsides to this solution. First, by using string keys, we restrict the property names of the elements we can work on. Usually I would choose to work with ['rows', 0, 'elements', 1, 'distance', 'text']
instead of 'rows|0|elements|1|distance|text'
. There are many good reasons, but that format makes for a much less convenient configuration. Note, though, that if you have property names that include '|'
or '#'
, some of this code would have to change.
Second, this solution requires you to rename leaf paths. If we wanted to rename 'rows|#|elements|#|duration'
to 'rows|#|elements|#|time'
, we would have to handle both the text
property and the value
property, even if we aren't changing them. I'd love to see a variant which doesn't require this, but I don't have a good alternative in mind.
In any case, I hope it works for your needs. And more, I hope others might offer better solutions.
这篇关于如何根据不同的客户端更改json对象的键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!