Skip to content Skip to sidebar Skip to footer

How To Change Keys Of Json Object Based On Different Client

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

Solution 1:

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 Ramdaconstpath = (ps = []) => (obj = {}) =>
  ps .reduce ((o, p) => (o || {}) [p], obj)

constassoc = (prop, val, obj) => 
  Number .isInteger (prop) && Array .isArray (obj)
    ? [... obj .slice (0, prop), val, ...obj .slice (prop + 1)]
    : {...obj, [prop]: val}

constassocPath = ([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 functionsconstgetPaths = (obj) =>
  Object (obj) === obj
    ? Object .entries (obj) .flatMap (
        ([k, v]) => getPaths (v) .map (p => k + (p ? '|' : '') + p)
      )
    : ['']

constpathEntries = (obj) => 
  getPaths (obj) .map (p => [p, path (p.split('|')) (obj)])

constfixPath = (p) =>
  p .split ('|') .map (n =>/^\d+$/.test(n) ? Number(n) : n)

consthydrate = (entries) =>
  entries .reduce ((a, [k, v]) => assocPath (fixPath (k), v, a), {})

constreconstruct = (fn) => (obj) => 
  hydrate (fn (pathEntries (obj)))


// Helper functionsconstaddHash = (config) => (key) =>
  key .split ('|') .map (k =>/^\d+$/ .test (k) ? '#' : k) .join ('|')

constconvertKey = (config) => (key, hashed = addHash (config) (key)) =>
  hashed in config ? config [hashed] : hashed

constreconstituteKey = (template, key, parts = key .split ('|')) => 
  template .split ('|') .map ((k, i) => k == '#' ? parts [i] : k) .join ('|')


// Main functionconstconvertStructure = (config) => 
  reconstruct (pathEntries => pathEntries .map (
    ([k, v]) => [reconstituteKey (convertKey (config) (k), k), v])
  )


// Sample inputconst 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',
}

// Democonsole .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.

Post a Comment for "How To Change Keys Of Json Object Based On Different Client"