Create a JavaScript object containing placeholders and another JavaScript object that holds the substitutions to be used

I have a placeholder object named ${var_name}

{
  "name": "${title}",
  "description": "${description}",
  "provider": {
    "@type": "Organization"
  },
  "hasInstance": [
    {
      "@type": "instance",
      "mode": [
        "study",
        "online"
      ],
      "offers": {
        "@type": "Offer",
        "price": "${price}"
      }
    }
  ]
}

And I also have a substitutions object

{
  "title": "aaa",
  "description": "bbb \n ccc",
  "price": 100,
  "history": "ddd"
}

Once the placeholders are replaced, the result should be:

{
  "name": "aaa",
  "description": "bbb \n cc",
  "provider": {
    "@type": "Organization"
  },
  "hasInstance": [
    {
      "@type": "instance",
      "mode": [
        "study",
        "online"
      ],
      "offers": {
        "@type": "Offer",
        "price": 100
      }
    }
  ]
}

Here is the function with the following steps:

  • convert the object with placeholders to a string
  • then replace with the substitutions object
  • convert the string back to an object

The issue is: JSON.parse will fail with data that contains JSON escape characters like: ", \, /, \b, \f, \n, \r, \t

function replace(template, data) {
    let templateString = JSON.stringify(template);
    var placeholders = templateString.match(/\${([\w\.\:]+)}/g);

    placeholders.forEach(placeholder => {
        // convert ${var_name} to var_name, then assign to phText
        let phText = placeholder.substring(2,placeholder.length - 1);
        if(data[phText]) {
            templateString = templateString.replace(placeholder, data[phText]);
        }
    });

    return JSON.parse(templateString);
}

Answer №1

Instead of relying on JSON parsing, this function scans an object recursively to search for a specific pattern and replace it with a new value:

function scanObjectAndReplace(newValues, obj, key=false){
    if(key && typeof(obj[key])==='string'){
        let str = obj[key];
        let i1=str.indexOf("${"); 
        let i2=str.indexOf("}");
        if(i1>=0 && i2>i1) {
            let keyname = str.substring(i1+2, i2);
            let substr = str.substring(i1, i2+1);
            if(!(keyname in newValues)) {console.log(`key ${keyname} not exist in new valuse.`);return}
            if(i1==0 && i2==str.length-1) // if value include only the placeholder then set a new value
                obj[key] = newValues[keyname];
            else
                obj[key] = str.replace(substr,values[keyname]); // otherwise treat it as string replacment
            console.log(`oldval=${str}, newval=${obj[key]}, type=${typeof(obj[key])}`)
        }
    } else {
        let objval = key ? obj[key] : obj;
        if (Object.keys(Object(objval)).length>0) {
            for (const k of Object.keys(objval)){
                console.log(k);
                scanObjectAndReplace(newValues,objval, k);
            }
        }
    }
}
scanObjectAndReplace(new_values, obj); // calling the function

While a shorter version using regexp is possible, this function is designed for easier debugging.

Answer №2

It seems like your code is functioning correctly,

"description": "bbb \n ccc"

However, it should actually be like this:

"description": "bbb \\n ccc"

This is because you are dealing with a JSON string where control characters need to be escaped with a backslash \. Therefore, in order to display a backslash in the code, you need to write two backslashes.

function replace(template, data) {
  let templateString = JSON.stringify(template);
  var placeholders = templateString.match(/\${([\w\.\:]+)}/g);

  placeholders.forEach(placeholder => {
    // convert ${var_name} to var_name, then assign to phText
    let phText = placeholder.substring(2, placeholder.length - 1);
    if (data[phText]) {
      templateString = templateString.replace(placeholder, data[phText]);
    }
  });

  return JSON.parse(templateString);
}

let test=replace({
  "name": "${title}",
  "description": "${description}",
  "provider": {
    "@type": "Organization"
  },
  "hasInstance": [{
    "@type": "instance",
    "mode": [
      "study",
      "online"
    ],
    "offers": {
      "@type": "Offer",
      "price": "${price}"
    }
  }]
}, {
  "title": "aaa",
  "description": "bbb \\n ccc",       // <-- here
  "price": 100,
  "history": "ddd"
});
console.log(test);
console.log(test.description);

It may look unusual when using console.log() for an object, but a linebreak inside is actually working as intended.


Although I understand the main issue, I prefer not to modify the input.

That's perfectly fine. Alternatively, you can escape the characters before the replacement. One way to achieve this is by using JSON.stringify(), which handles the escaping for JSON formatting. The drawback is that it surrounds the string with quotation marks, which will need to be removed manually.

function replace(template, data) {
  let templateString = JSON.stringify(template);
  var placeholders = templateString.match(/\${([\w\.\:]+)}/g);

  placeholders.forEach(placeholder => {
    // convert ${var_name} to var_name, then assign to phText
    let phText = placeholder.substring(2, placeholder.length - 1);
    let rpText = data[phText];
    if (rpText) {
      rpText = JSON.stringify(rpText);             // <-- here
      rpText = rpText.substr(1,rpText.length-2);   // <-- and here
      templateString = templateString.replace(placeholder, rpText);
    }
  });

  return JSON.parse(templateString);
}

let test=replace({
  "name": "${title}",
  "description": "${description}",
  "provider": {
    "@type": "Organization"
  },
  "hasInstance": [{
    "@type": "instance",
    "mode": [
      "study",
      "online"
    ],
    "offers": {
      "@type": "Offer",
      "price": "${price}"
    }
  }]
}, {
  "title": "aaa",
  "description": "bbb \n ccc",       // <-- and not here
  "price": 100,
  "history": "ddd"
});
console.log(test);
console.log(test.description);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to pass data from a parent component to a child component and utilize it within a function in Next.js

As a beginner in react and nextjs, I am struggling with passing the "response" from a function in the parent component to another component (component1). function func1(props) { function handleSubmit(e) { e.preventDefault(); v ...

The malfunctioning jQuery dropdown menu on hover that needs fixing

Currently, I have an HTML link that utilizes the hover() function (specifically using the hoverIntent plugin) to prompt a div to slide down by animating the CSS top attribute. Everything is functioning as expected, except when the cursor transitions from ...

Managing cookie-related changes to CSS classes using jQuery can present challenges

I stumbled upon an interesting solution for changing background color and storing it in a cookie by utilizing jquery.cookie. After making a few tweaks, it worked smoothly: $(document).ready(function () { $("body").css("background-image",$.cookie("<?ph ...

Hierarchy-based dynamic breadcrumbs incorporating different sections of the webpage

Currently in the process of developing a dynamic breadcrumb plugin with either jQuery or Javascript, however I am struggling to implement functionality that allows it to change dynamically as the page is scrolled. The goal is to have a fixed header elemen ...

What is the best way to display my to-do list items within a React component

I'm working on a straightforward todo application using fastapi and react. How can I display my todos? I attempted to use {todo.data}, but it's not functioning as expected. Here is my Todos.js component: import React, { useEffect, useState } fro ...

Passing an array from JavaScript to PHP and then storing it as a .json file

Query I am trying to pass an array to PHP and save it in a data.json file. However, the data.json file is being created but showing Null as output. I have spent about 2 hours on this code, checked numerous solutions on SO, but nothing seems to work. As I ...

Creating an Ajax Post request for API invocation

I'm currently setting up an Ajax Post request to interact with an API. Here is a mock curl command for reference: curl -X POST \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --h ...

how can I include an AngularJS variable as a parameter in an onclick function?

I attempted to utilize an AngularJS variable as an argument value inside onclick() in order to invoke a JavaScript function. Can someone provide me with instructions on how to achieve this? Here is my code: <div onclick="deleteArrival({{filterList.id} ...

What is the method utilized by Redux to store data?

I am currently developing a small application to enhance my understanding of how to utilize redux. Based on my research, redux allows you to store and update data within the store. In my application, I have implemented an HTML form with two text inputs. Up ...

Automated vertical alignment of rows within the Bootstrap table

I'm currently working on a table for my personal project that populates with data from the database. I am trying to get the rows to display vertically under headings (see screenshot at the bottom of this question). I have attempted various solutions f ...

Ways to ensure the axios call is completed before proceeding with the codeexecution

I need to use axios to send a request and retrieve some data that I want to pass as a prop to a React Component. Here's my render function: render() { boards = this.fetchBoardList(); return ( <div className="app-wrapper"> ...

Create an array or map of JSON encoder types in Golang

As a newcomer to Golang, I recently faced a challenge with the code below: // XXX a bit inefficient. could open r files and run over list once for r := 0; r < nreduce; r++ { file, err = os.Create(ReduceName(fileName, JobNumber, r)) if err != ni ...

Stop the flow of data in the RxJS stream depending on a specific value within the stream

I developed a straightforward component featuring a single button that initiates and halts a sequence of numbers generated by RxJS timer. import { Component, OnInit } from '@angular/core'; import { BehaviorSubject, Observable, timer, merge } fro ...

Retrieve the most recent ajax request and cancel any other ones

I've been exploring this issue and it seems fairly straightforward, but I'm having trouble finding a solution. I have several requests that call different URLs. However, for each URL, I only want to receive the last result from the same URL being ...

The issue of JQuery selector failure within an AngularJS controller

In my current setup, I have viewA along with ControllerA. However, when an image is clicked on viewA, I need to switch over to another ViewB along with its corresponding ControllerB. In ViewB, there are multiple checkboxes which the user can interact wit ...

Getting started with TinyMCE in Nuxt: A step-by-step guide

I need to incorporate this script into my Nuxt code: <script> tinymce.init({ selector: "#mytextarea", plugins: "emoticons", toolbar: "emoticons", toolbar_location: "bottom", menubar: false ...

Acquire JSON information from PHP utilizing JavaScript

I am a beginner with JSON and I'm retrieving data from PHP code in JSON format. [ { "Title": "New Event", "TYPE": "info", "StartsAt": "16 November 201512:00", "EndsAt": "25 November 201512:00" }, { ...

Retrieving Blocked Images with Selenium: A Step-by-Step Guide

HTML: <html> <head> <body onload="document.getElementById('a').style.display='block';"> <div id="a" align="center" onclick="document.location.reload();" style="display: block; cursor: pointer;"> <img width="9 ...

Response from a Clean jQuery Web Service

I've been seeing a lot of code examples calling web services that return JSON data, but they tend to involve back-end languages like PHP. Does anyone know of a tutorial for a jQuery-only solution? For example, setting up div tags with IDs, then direct ...

What is the purpose of the execute_script() function in Selenium?

browser.execute_script("window.open('about:blank', 'tab2');") browser.switch_to.window("tab2") browser.get('http://bing.com') While exploring ways to open a new tab using Selenium in Python, I found the ab ...