javascript - substitute into a string template, but catch missing variables - javascript

I want to carry out variable substitutions on a string (I've already ruled out template literals because the string has to be stored and evaluated later).
Mustache or something like it would seem like a contender, but I want to know if the substitution was incomplete. In this case, it's to produce urls, so missing parts mean invalid urls:
Testing this out in node:
var Mustache = require('mustache');
var result = Mustache.render("/someurl/{{path1}}/{{path2}}/", {path1:"my-path-to-1"})
console.log(`result:${result}:`)
This happens without a problem, but the resulting url is useless because Mustache silently replaced the missing path2 with an empty string. What I would like to see is an exception getting thrown (best) or failing that an easy way to recognize that not everything was substituted.
Note: the template string is arbitrary and so are the substitution object's contents.
output:
result:/someurl/my-path-to-1//:
This is the Python equivalent that I am looking for:
res = "/someurl/%(path1)s/%(path2)s/" % {"path1":"my-path-to-1"}
output:
KeyError: 'path2'

I ended up using sprintf, which has the benefit of having a different format than Mustache (or Django) so that you can embed it in data-url or the like:
const sprintf = require("sprintf-js").sprintf;
var o_substit = {
path1 : "mypath1"
};
var T_URL = "/someurl/%(path1)s/%(path2)s/";
console.log(`\nT_URL:${T_URL}, substitutions:`);
try {
console.dir(o_substit);
console.log("expecting a failure...")
var url = sprintf(T_URL, o_substit);
console.log(` url:${url}:`);
}
catch (e){
console.log(` exception:${e}`);
};
var o_substit = {
path1 : "mypath1"
,path2 : "mypath2"
};
console.log(`\nT_URL:${T_URL}, substitutions:`);
try{
console.dir(o_substit);
console.log("\nexpecting a success:")
var url = sprintf(T_URL, o_substit);
console.log(` url:${url}:`);
}
catch (e){
console.log(` exception:${e}`);
};
output:
T_URL:/someurl/%(path1)s/%(path2)s/, substitutions:
{ path1: 'mypath1' }
expecting a failure...
exception:Error: [sprintf] property 'path2' does not exist
T_URL:/someurl/%(path1)s/%(path2)s/, substitutions:
{ path1: 'mypath1', path2: 'mypath2' }
expecting a success:
url:/someurl/mypath1/mypath2/:

Related

Convert string with '=' to JSON format

I am trying to convert a string i receive back from an API into a JSON object in Angular.
The issue is that the string is not normalized to be parsed into JSON easily.
This is the string im working with:
"{rootCause=EJBusinessException: This is a sample exception thrown for testing additional info field, description=This is a more detailed description about the incident., stackTrace=com.springboot.streams.infrastructure.web.heartbeat.HeartbeatService.testServiceNow(HeartbeatService.java:200)}"
When trying to do JSON.parse(myStr) it throws an error due to invalid string format.
Is there an easy way to convert the listed string into a more correct JSON format, getting rid of the '=' and replacing them with ':' instead.
There is more to it than just .replace(/['"]+/g, ''), as even with that the string is not ready to be turned into JSON yet.
Hoping someone more versed in Javascript knows a trick i dont.
You just need to manipulate the string before parsing it remove unecessary string that can cause error to the object like "{" and "}" and split it by "," example is in below.
var obj = {}, str = "{rootCause=EJBusinessException: This is a sample exception thrown for testing additional info field, description=This is a more detailed description about the incident., stackTrace=com.springboot.streams.infrastructure.web.heartbeat.HeartbeatService.testServiceNow(HeartbeatService.java:200)}"
str.split(",").forEach((st, i) => {
pair = st.split("=")
if(pair.length > 1) {
obj[pair[0].replace("{",'').replace("}", '').trim()] = pair[1]
} else {
obj[i] = pair
}
})
console.log(obj)
As commenters have posted, unless you control the API or at least have documentation that output will always follow a specific format, then you are limited in what you can do. With your current example, however you can trim off the extraneous bits to get the actual data... (remove braces, split on comma, split on equals) to get your key:value pairs... then build a javascript object from scratch with the data... if you need json string at that point can just JSON.stringify()
var initialString = "{rootCause=EJBusinessException: This is a sample exception thrown for testing additional info field, description=This is a more detailed description about the incident., stackTrace=com.springboot.streams.infrastructure.web.heartbeat.HeartbeatService.testServiceNow(HeartbeatService.java:200)}"
var trimmedString = initialString.substr(1, initialString.length - 2);
var pairArray = trimmedString.split(',');
var objArray = [];
pairArray.forEach(pair => {
var elementArray = pair.split('=');
var obj = {
key: elementArray[0].trim(),
value: elementArray[1].trim()
};
objArray.push(obj);
});
var returnObj = {};
objArray.forEach(element => {
returnObj[element.key] = element.value;
});
console.log(JSON.stringify(returnObj));

javascript is there a way to clean up a string multiple times in a clean way

I'm trying to clean up a JSON string (that was converted from a json object) and I found that many people use the .replace() method to do so. However, in doing so my code looked like this:
scrape(url).then(result => {
final = JSON.stringify(result);
final = final.replace(/['"]+/g, "");
final = final.replace(/[{]+/g, "");
final = final.replace(/[}]+/g, "");
final = final.replace(/[:]+/g, ": ");
final = final.replace(/,+/g, ";");
return final;
});
While this method does work returning 'final' in the way i want it, it does not seem very efficient and the code is really clunky. My end goal is to remove quotes, curly brackets, replace ':' with ': ' and change all commas to semi colons. Is there a better/cleaner way to do this?
EDIT:
The input string looks something like this:
{
'$primary': '#ea80fc',
'$p_light': '#ffb2ff',
'$p_dark': '#b64fc8',
'$secondary': '#b64fc8',
'$s_light': '#f9683a',
'$s_dark': '#870000'
}
Given your actual data, where after JSON.parse you have the following structure:
{
'$primary': '#ea80fc',
'$p_light': '#ffb2ff',
'$p_dark': '#b64fc8',
'$secondary': '#b64fc8',
'$s_light': '#f9683a',
'$s_dark': '#870000'
}
turning this into legal SCSS doesn't require a long chain of replaces applied to the JSON string at all. It just requires parsing the JSON to plain JS object, and then iterating over the key/values to form an SCSS string:
function jsonToSCSS(stringdata=``, data={}) {
/* JSON.parse can throw. Always be ready for that. */
try { data = JSON.parse(stringdata); }
catch (e) { console.warn(e); return ``; }
return Object.keys(data)
.map(key => `${key}: ${data[key]};`)
.join('\n');
}
And done. The output of that function is now a normal, formatted string:
$primary: #ea80fc;
$p_light: #ffb2ff;
$p_dark: #b64fc8;
$secondary: #b64fc8;
$s_light: #f9683a;
$s_dark: #870000;
Which you can now write into whatever file you need it written into, either directly, or itself wrapped in formatting:
const SCSS = jsonToSCSS(inputdata);
const qualified = `.someclass { ${SCSS} }`;
More simplified
scrape(url).then(result => {
return JSON.stringify(result.replace(/['"{}]+/g, "").replace(/[:]+/g, ":").replace(/,+/g, ";"));
});

Parsing a JSON sub string

I have javascript function that calls an external Api and returns in most case a valid JSON string.
function (successResponse) {
{
console.log(successResponse);
}
However, in some cases it return the the following invalid JSON
Response: Status=200, Text: {"createdTime":"2017-05-08T14:47:56Z","lastUpdatedTime":"2017-05-08T14:47:56Z","createdMode":"API","uuid":"e333c1-3599-36d7-9ef5-dc22c79a4a52","userId":"anonymous"}, Error Message: null
How can I parse the above string to get the 'uuid'
Thanks
If you're expecting a response string in that format, you can use a regular expression to extract the "text" portion of the response:
function (successResponse) {
{
var responseText = successResponse.match(/\{.+\}/);
var responseTextJSON = JSON.parse(responseText);
var uuid = responseTextJSON.uuid;
console.log(uuid);
}
Maybe you can parse the string yourself to exclude everything outside of {} ?
var apiResponse = 'Response: Status=200, Text: {"createdTime":"2017-05-08T14:47:56Z","lastUpdatedTime":"2017-05-08T14:47:56Z","createdMode":"API","uuid":"e333c1-3599-36d7-9ef5-dc22c79a4a52","userId":"anonymous"}, Error Message: null';
var apiResponse_fixed = apiResponse.substring((apiResponse.indexOf("{") - 1), (apiResponse.lastIndexOf("}") + 1));
var json_obj = JSON.parse(apiResponse_fixed);
console.log(json_obj.uuid);
Replace the non-JSON features, and then interpret as JSON
Looks like the server owner has been a bit lazy, and programmed an error response which contains a JSON-like interior section but surrounded by a couple of non-JSON elements.
If you are desperate to resolve the situation and have no ability to fix the server output format, here is my suggestion:
notQuiteJson = 'Response: Status=200, Text: {"createdTime":"2017-05-08T14:47:56Z","lastUpdatedTime":"2017-05-08T14:47:56Z","createdMode":"API","uuid":"e333c1-3599-36d7-9ef5-dc22c79a4a52","userId":"anonymous"}, Error Message: null';
madeJson = notQuiteJson.replace('Response: Status=200, Text:','{"Response": {"Status":200}, "Text":').replace('Error Message: null','"ErrorMessage": null}')
obj = JSON.parse(madeJson)
console.log(obj.Text.uuid) // Result: "e333c1-3599-36d7-9ef5-dc22c79a4a52"
Of course this only works if the error message is always exactly this. In reality you may want to use a 3-digit wildcard to cover a range of "Status=" codes. But then you would have to also be confident that all the error modes produce the same non-JSON text at the start and end of the response.
Disclaimer
#sp00m and #Bergi, don't kill me: you are right of course, but this is just for if the poster has no choice in the matter 8-)

Regular Expression: pull part from string. with JS

Hey all im not every good with regexp i was hoping someone could help.
ok so this is the sting "KEY FOUND! [ 57:09:91:40:32:11:00:77:16:80:34:40:91 ]"
And i need to pull "57:09:91:40:32:11:00:77:16:80:34:40:91", now this key can be meany length not just as written here and with or with out the ":"
now the second sting i would like to test and extract is: "[00:00:09] Tested 853 keys (got 179387 IVs)", i would like to pull "00:00:09" and "853" and "179387".
this would be the raw string http://regexr.com?31pcu or http://pastebin.com/eRbnwqn7
this is what im doing now.
var pass = new RegExp('KEY FOUND\!')
var tested = new RegExp('Tested')
var fail = new RegExp('\Failed. Next try with ([0-9]+) IVs')
var data="Look at the link i added"
if (tested.test(data)) {
self.emit('update', mac, {
'keys' : data.split('Tested ')[1].split(' keys ')[0],
'ivs' : data.split('got ')[1].split(' IVs')[0]
});
} else if (pass.test(data)) {
var key = data.split('KEY FOUND! [')[1].split(' ]')[0].split(':').join('');
} else if (fail.test(data)) {
console.log(data);
}
thanks all
Edit:
I have added more the the question to help with the answer
If it is always surrounded by [] then it is simple:
\[([\s\S]*)\]
This will match any characters enclosed by [].
See it in action here.

Ambiguous interface of RegExp

Something very strange.
var body="Received: from ([195.000.000.0])\r\nReceived: from ([77.000.000.000]) by (6.0.000.000)"
var lastMath="";
var subExp = "[\\[\\(](\\d+\\.\\d+\\.\\d+\\.\\d+)[\\]\\)]"
var re = new RegExp("Received\\: from.*?"+subExp +".*", "mg");
var re1 = new RegExp(subExp , "mg");
while(ares= re.exec(body))
{
print(ares[0])
while( ares1 = re1.exec(ares[0]))
{
if(!IsLocalIP(ares1[1]))
{
print(ares1[1])
lastMath=ares1[1];
break ;
}
}
}
print(lastMath)
It outputs:
Received: from ([195.000.000.0])
195.000.000.0
Received: from ([77.000.000.000]) by (6.0.000.000)
6.0.000.000
6.0.000.000
But I think it should be:
Received: from ([195.000.000.0])
195.000.000.0
Received: from ([77.000.000.000]) by (6.0.000.000)
77.000.000.000
77.000.000.000
Because obviously "77.000.000.000" goes first. If I comment "break", output order is correct.
What's wrong with my code?
Note that regex grouping in Javascript (and most languages) does not work with a very obvious behavior with the * or + operators. For example:
js>r = /^(ab[0-9])+$/
/^(ab[0-9])+$/
js>"ab1ab2ab3ab4".match(r)
ab1ab2ab3ab4,ab4
In this case, you get the last group that matches and that's it. I'm not sure where this behavior is specified, but it can vary from language to language.
edit: What does IsLocalIP() do?
OK, I think the problem has to do with exec's statefulness (which may be why I don't use it; I use String.match()) -- if you're going to do this, you need to manually initialize the regex's lastindex property to 0, otherwise you get this behavior:
function weird(dobreak)
{
var s = "Received: from ([77.000.000.000]) by (6.0.000.000)"
var re1 = /[\[\(](\d+\.\d+\.\d+\.\d+)[\]\)]/mg
while (s2 = re1.exec(s))
{
writeln("s2="+s2);
if (dobreak)
break;
}
}
produces this result:
js>weird(true)
js>weird(true)
s2=[77.000.000.000],77.000.000.000
js>weird(true)
s2=(6.0.000.000),6.0.000.000
js>weird(true)
js>
You'll note that the same function gets three different results, which implies statefulness is mucking things up for some bizarre reason (Javascript is caching/interning the regex somehow? I'm using JSDB which uses Spidermonkey = Firefox's javascript engine).
So if I change the code to the following:
function notweird(dobreak)
{
var s = "Received: from ([77.000.000.000]) by (6.0.000.000)"
var re1 = /[\[\(](\d+\.\d+\.\d+\.\d+)[\]\)]/mg
re1.lastIndex = 0;
while (s2 = re1.exec(s))
{
writeln("s2="+s2);
if (dobreak)
break;
}
}
Then I get the expected behavior:
js>notweird(true)
s2=[77.000.000.000],77.000.000.000
js>notweird(true)
s2=[77.000.000.000],77.000.000.000
js>notweird(true)
s2=[77.000.000.000],77.000.000.000

Categories

Resources