JSON recursive value substitution against another object

recursively iterate json object java
update nested json object java
replace json value in java
json.stringify replacer
replace value in json object javascript
how to get key and value from json array object in javascript
how to get value from nested json object in java example
json object methods

I have a json object which consist of arrays, and maps with some placeholder text that can be substituted by passing another object.

Eg.

data = {
  "name": "Hello ${user.name}",  
  "primary_task": "Task Name: ${user.tasks[0].name}",
  "secondary_tasks": ["Task 2: ${user.tasks[1].name}", "Task 2: ${user.tasks[2].name}"]
}

Variables or metadata object can be

variables = {
  "user": {
    "name": "DJ"
  },
  "tasks": [
    {
      "name": "Task One"
    }
  ]
}

I have a function that can take the string and subtitute based on some object. I am not sure how to invoke this on a JSON object recursively so that it can substitute all the string values in map and array

var data = {
  "name": "Hello ${user.name}",  
  "primary_task": "Task Name: Task One",
  "secondary_tasks": ["Task 2: ${user.tasks[1].name}", "Task 2: ${user.tasks[2].name}"]
}

var metadata = {
  "user": {
    "name": "DJ",
    "tasks": [
    {
      "name": "Task One"
    }
  ],
  },  
}

function subString(str) {
  var rxp = /\{([^}]+)\}/g,    
    liveStr = str,
    curMatch;

while( curMatch = rxp.exec( str ) ) {
    var match = curMatch[1];
    liveStr = liveStr.replace("${"+ match + "}", tryEval(match));    
}
return liveStr;
}


function tryEval(evalStr) {
  evalStr = "metadata." + evalStr;
  try {
  return eval(evalStr);
}
catch(error) {
  return "${" + evalStr + "}";
}

}
var str = "user ${user.name} - ${user.tasks[0].name} - ${user.tasks[2].name}";

console.log("Sub " + subString(str));

In the above example, ${user.tasks[2].name} does not exist in meta so it should not resolve to undefined. If the key is not found in meta object, it should leave that as is i.e. ${user.tasks[2].name}

Using ES6 only scenario:

const data = { "name": "Hello ${user.name} ${user.foo}", "primary_task": "Task Name: ${user.tasks[0].name} ${user.tasks[10].name}", "secondary_tasks": ["Task 2: ${user.tasks[0].name}", "Task 2: ${user.tasks[1].name}", "Task 3: ${user.tasks[11].name}"] }
const meta = { "user": { "name": "DJ", "tasks": [ { "name": "Task One" }, { "name": "Task Two" } ] } }

const getPath = (path, obj) => path.split('.').reduce((r, c) =>
  r ? c.includes('[') ? getPath(c.replace('[', '.').replace(']', ''), r) : r[c] : undefined, obj)

const interpolate = (s, v) =>
  new Function(...Object.keys(v), `return \`${s}\`;`)(...Object.values(v))

const templ = (str, obj) => {
  let r = new RegExp(/\${([\s\S]+?)}/g)
  while (match = r.exec(str)) {
    if (!getPath(match[1], obj))
      str = str.replace(match[0], match[0].replace('${', '__'))
  }
  return interpolate(str, obj).replace('__', '${')
}

const resolve = (d, vars) => {
  if (Array.isArray(d))
    return d.map(x => templ(x, vars))
  else
    return Object.entries(d).reduce((r, [k, v]) =>
      (r[k] = Array.isArray(v) ? resolve(v, vars) : templ(v, vars), r), {})
}

console.log(resolve(data, meta))

JSON recursive value substitution against another object, I have a json object which consist of arrays, and maps with some placeholder text that can be substituted by passing another object. Eg. In another article here, entitled on JSON and SQL, we saw in great details how to import a data set only available as a giant JSON file. Then we normalized the data set, so as to be able to write SQL and process our data. This approach is sometimes very useful and was a good way to learn some of the JSON functions provided by PostgreSQL. In this article, we’re going to use SQL to export the

Here is a solution with pure JS by simply using JSON.stringify, regex, match, replace, reduce and some other methods.

Something that did cause issues, is that your "variables" variable doesn't seem to have the correct format. "tasks" should be a child of "user". I've fixed that for you if that is indeed what you intended.

This is definitely optimizable so let me know if there is anything to simplify.

Solution:

data = {
  "name": "Hello ${user.name}",
  "primary_task": "Task Name: ${user.tasks[0].name}",
  "secondary_tasks": ["Task 2: ${user.tasks[1].name}", "Task 2: ${user.tasks[2].name}"]
}

variables = {
  "user": {
    "name": "DJ",
    "tasks": [{
        "name": "Task Primary"
      },
      {
        "name": "Task One"
      },
      {
        "name": "Task Two"
      }
    ]
  }
}

const str = JSON.stringify(data);
const reg = /\$\{([a-z]|\[\d\]|\.)+\}/gi

const res = str.match(reg).reduce((acc, cur) => {
  //slice to remove ${ and }
  const val = cur.slice(2, -1).split(".").reduce((acc2, cur2) => {
    //check to see if it's like for example: tasks[1]
    if (cur2.indexOf("[") > -1) {
      const s = cur2.split("[");
      //Ex: acc2["tasks"][0]
      //slice to remove trailing "]"
      return acc2[s[0]][s[1].slice(0,-1)];
    }
    //Ex acc2["user"]
    return acc2[cur2];
  }, variables);
  //val contains the value used to replace the variable string name
  return acc.replace(cur, val);
}, str);

console.log(JSON.parse(res))

How to iterate through a JSON object to find/replace, Hello, this question is regarding parsing JSON to modify an The reason for recursion is that the hierarchy of the response is different between different I am open to various possibilities on how something like this can be fixed. key) { var value = parent[key], w = what.call(value); if ((w == "[object  Find values in JSON by key, recursively. GitHub Gist: instantly share code, notes, and snippets.

An approach using the loadash utility library:

var _ = require('lodash'); // use for node
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/0.10.0/lodash.min.js"></script>; // use for browser 

var data = {
"name": "Hello ${user.name}",
"primary_task": "Task Name: Task One",
"secondary_tasks": ["Task 2: ${user.tasks[0].name}", "Task 2: ${user.tasks[1].name}"]
}

var metadata = {
"user": {
    "name": "DJ",
    "tasks": [{
            "name": "Task One"
        },
        {
            "name": "Task Two"
        }
    ],
},
}
var text = JSON.stringify(data); // stringify data object
var myregexp = /\${([\[\]a-z\d.]+)}/i // regex to match the content to be replaced in data
while (match = myregexp.exec(text)) { // loop all matches
try {
    // Example: [0]=${user.name} / [1]=user.name
    new_data = text.replace(match[0], _.get(metadata, match[1])); // replace values using _ library
    text = new_data;
} catch (err) {
    console.log("Requested element doesn't exist", err.message);
}
match = myregexp.exec(text);
}
var new_data = JSON.parse(new_data); // convert new_data to object
console.log(new_data);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

Manipulating Nested JSON Values in Java Using the org.json Library, The org.json library provides a convenient way to convert Java Luckily, this can be accomplished fairly painlessly with recursion. To get each individual key, we can split the String based on the leading slash before each key. we only make it this far if every other key in keys pointed to existing values. Validating config-files with a schema and filling in default-values and handling the required-array in a recursive way will simplify a lot the code actually using the config-file. Maybe a schema should not be valid if a sub-object has a default value but the parent one has not.

You can try this. It is basically a function (get) which gets you the value (if any) following the path, and a function (replaceWhereDefined) which given a value, will replace it on your data. Hope this helps.

const data = {"name": "Hello ${user.name}", "primary_task": "Task Name: Task One", "secondary_tasks": ["Task 2: ${user.tasks[0].name}", "Task 2: ${user.tasks[2].name}"]};

const meta = {"user": {"name": "DJ", "tasks": [{"name": "Task One"}] } };

const get = (s, meta) => {

    const parts = s.replace(/(\${)|}/g, '').split('.');

    const value = parts.reduce((acc, val) => {

        const isArray = val.match(/\[\d+]/g);

        if (isArray) {

            const arr = val.match(/[a-zA-Z]+/g).toString();

            const position = isArray.toString().replace(/[\[\]]/g, '');

            acc = acc[arr];

            if (acc) acc = acc[position];
        }

        else acc = acc[val];

        return acc || {};

    }, meta);

    return typeof value === 'string' ? value : null;
};

const replaceWhereDefined = (data, meta) =>

    Object.keys(data).reduce((acc, key) => {

        const toReplace = data[key].toString().match(/\${.*?}/g);

        if (toReplace) {

            toReplace.forEach((path) => {

                const value = get(path, meta);

                if (Array.isArray(acc[key]) && value) {

                    acc[key] = acc[key].map((d) => d.replace(path, value));
                }

                else if (value) acc[key] = acc[key].replace(path, value);
            });
        }

        return acc;

    }, data);

console.log(replaceWhereDefined(data, meta));

JSON.stringify(), The JSON.stringify() method converts a JavaScript object or value to If you return any other object, the object is recursively stringified, calling the replacer function on Therefore, if compatibility with older JavaScript engines is required, it is perilous to directly substitute the string returned by JSON.stringify  JSON Pointer aims to serve the same purpose as XPath from the XML world, but it is much simpler. If you’re using a definition from the same document, the $ref value begins with the pound symbol ( # ). Following that, the slash-separated items traverse the keys in the objects in the document.

End-User Development: 7th International Symposium, IS-EUD 2019, , Another benefit of S-expressions is the original argument for this notation: rules​: – JSON notation defines objects, arrays, strings, numbers and the values true, Each element of the list can be an atomic value or a list, and so on recursively. The interpretation relies on a list of predefined functions and string substitutions  Because all keywords apply when combining schemas, there would be no JSON value that could ever be valid against that schema. The Person schema is asserting that there can be no properties other than "firstName" and "lastName", but the Employee schema is asserting that the "company" schema is required.

Pro Functional PHP Programming: Application Development Strategies , Your JSON string is likely to have characters that aren't valid for file names, to parse the serialized string and substitute invalid characters, and so on, in a consistent way, ensuring that you stay within other limits for grab its return value and create the cache file, and finally return that grabbed value as your return value. In my above JSON string, there is a property called "hasmore" and in all the JSON response this field will be there always so if "hasmore" is true, then it means there are more IP Address which will be there in another URL and that URL is present in "next" JSON object in the same JSON response at the bottom so I need to execute that URL and get

jq Manual (development version), Instead of running the filter for each JSON object in the input, read the entire input Another way to set the exit status is with the halt_error builtin function. You can also use this on an object, and it will return all the values of the object. them recursively: this works like addition but if both objects contain a value for the​  Traverse the json object recursively. When traversing each entry of the json object, check if it contains a key or value with the type of string, if so, capitalize the key or the value accordingly.

Comments
  • I wrote a resolve property function, you could extend it to also work with arrays. stackblitz.com/edit/typescript-fchtqv
  • Why was the app designed like this in 1st place?
  • Where does data and metadata come from? How do the ${} bits get into the data? I assume you are not hard coding the data? Are there any parameters defining structure of metadata? What if data has a field metadata does not?
  • The metadata.user.tasks has only one item in it, which is why "Task 2: ${user.tasks[2].name}" isn't being replaced (user.tasks[2] doesn't exist). What is your expected output?
  • i like your solution but if the key doesn't exist in meta object, it will resolve to undefined. For eg: if user.tasks[1].name doesn't exist in meta object, it should leave it as that instead of setting as undefined.
  • @ed1t both solutions updated to support your requirement.
  • If the key doesn't exist in meta object, it will resolve to undefined. For eg: if user.tasks[1].name doesn't exist in meta object, it should leave it as instead of setting as undefined. My original function left it as ${user.tasks[1].name}
  • why using eval? It is NOT needed and potentialy harmfull
  • As stated in the commented code, I couldn't find a better solution for this problem. If you have an alternative to eval, feel free to show it.
  • sure you can traverse the metadata struct based on the matched key. The matched key is either object-based ie param.param2` (split on .) and/or array-based ie param1[index2] where you split on [] and match the index. It is more work than eval but definately better to use
  • first you transform the array-based indexes to object-based usign a regex like matchKey.replace(/\[([^\[\]])\]/, ".$1"); then trim leading/trailing dots matchedKey = matchKey.replace(/^\.|\.$/, ""); then uyou split on dots and traverse the metadata struct based on each sub-key, if no key exists then return an error else return the final value. This should work
  • that lodash function implements what I have described in my comment, but better yet isolate the code from lodash and use it as an autonomus function to avoid including 3rd-pary libraries only for a function