Replace multiple values in file loop - javascript

I'm trying to build a quick and dirty static site generator for myself.
Let's say I have this test.html file:
{title}
{downloadpath}
This is my current.json where I get the values i want to replace:
{
"id": 123,
"album" : [{
"title": "Test EP",
"albumid": 1234,
"path": "test.zip"
}]
}
My replacement function looks like this:
// Iterate through JSON object and replace
function iterate(obj) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object")
iterate(obj[property]);
else
console.log("replace {" + property + "} with " + obj[property] )
htmldata.replace(/\{property\}/g, obj[property]);
}
}
}
iterate(json)
var result = htmldata
console.log(result)
// Write new HTML
fs.writeFile("test-" + json.id + ".html", result, 'utf8', function (err) {
if (err) {
return console.log(err);
}
});
and if I run it it works like this:
replace {id} with 123
replace {title} with Test EP
replace {albumid} with 1234
replace {path} with test.zip
{title}
{path}
and you can see the problem right there. I think it's always replacing the edited file with the input file so I don't see any changes. I can't figure it out and if someone could point me in the right direction I'd be grateful.
Thanks!

Not using braces around your if statements will lead to subtle bugs!
You want:
if (typeof obj[property] == "object") {
iterate(obj[property]);
} else {
console.log("replace {" + property + "} with " + obj[property] )
htmldata.replace(/\{property\}/g, obj[property]);
}
Otherwise the replace will run every time regardless of the condition on the if.
Second thing: your regex tries to match the literal string "{property}". Instead, try this:
htmldata.replace(new RegExp("{" + property + "}", "g"), obj[property]);
Third thing: you're not assigning the result of the replace back to htmldata. So you need to do this:
htmldata = htmldata.replace(new RegExp("{" + property + "}", "g"), obj[property]);

Related

How to replace specific parts of a json file using node.js

Currently, I have a JSON file that looks like this:
"1E10BC5D4EC68EE2916BFD97F23E951C": "Seattle Seahawks",
"E6B87019417436B73B62F7802763A289": "Seaside style. ",
"81EEA9E6400BFEADE161559AF14EE468": " {1}",
"6F02148E4A78B33C1CEB75BC2753CA69": " {EndDate}",
"D89CA2634FFF8FA02D028096BAAE6963": "\"You have received a reward for completing the {Bundle Name} {Number} challenges!",
"Mission": {
"Default Mission Info Description": "Default Mission Description",
"Default Mission Info Name": "Default Mission Name",
"RewardDescription": "You ranked {PositionRank}. For your efforts, you have been awarded:",
"NonParticipationRewardDescription": "Your teammates did a great job! For their efforts, you have been awarded:",
"RewardTitle": "{MissionName} Completed!"
}
It goes on for about 40,000 lines, and I would like to modify all of the strings set by it. Currently, I am using #zuzak/owo to try to accomplish this. My current code looks like this:
const owo = require('#zuzak/owo')
fs = require('fs');
var name = '../jsonfile.json';
var data = fs.readFileSync(name).toString();
fs.writeFileSync('../owo.json', JSON.stringify(owo(data)))
How would I be able to only change the strings, such as "Seaside style. " and not edit any of the string names, such as "81EEA9E6400BFEADE161559AF14EE468" (sorry for any incorrect wording, hopefully you can understand what I am saying.)
In case you need it, here is the main code used in #zuzak/owo:
const addAffixes = (str) => randomItem(prefixes) + str + randomItem(suffixes)
const substitute = (str) => {
const replacements = Object.keys(substitutions)
replacements.forEach((x) => {
str = replaceString(str, x, substitutions[x])
})
return str
}
Parse the JSON, iterate over keys, modify values. If necessary recursively iterate over subobjects too:
function owoifyObject (obj) {
for (const key in obj) {
if (typeof obj[key] === 'string') {
obj[key] = owo(obj[key])
} else if (typeof obj[key] === 'object' && obj[key]) {
owoifyObject(obj[key])
}
}
}
const data = JSON.parse(fs.readFileSync('file.json').toString())
owoifyObject(data)
fs.writeFileSync('file2.json', JSON.stringify(data, null, 4))
Note that the argument 4 in JSON.stringify is purely there for formatting so that the result looks something like your input did, otherwise you'd get all the data in a single line.
Use JSON.parse() to parse the JSON file into an object. Then go through the object calling owo() on each value.
var data = JSON.parse(fs.readFileSync(name).toString());
for (let key in data) {
data[key] = owo(data[key]);
}
fs.writeFileSync("../owo.json", JSON.stringify(data));

Parse Specific Json in JavaScript

Im struggling to parse Json for a desired need.
I am extracting Data from a GraphQL call, and need to parse the response and then generate an XML file.
Example JSON Resonse :
{
"data": {
"products": {
"results": [{
"masterData": {
"current": {
"name": "Paragon Pen",
"description": "TEST",
"categories": [{
"name": "Writing Instrument"
}]
}
}
}]
}
}
}
I need to parse this and make an xml file like this:
<?xml version="1.0"?><product><name>Paragon Pen</name><description>TEST</description><category>Writing Instrument</category></product>
I cant get a method to parse and then hold onto the parent element.
For example, i want to make the xml tag and then add the 'name' element (Writing Instrument) inside this tag. Any help is appreciated.
My Parse method so far.
:
//extractJSON function. Get the information and write it to the file.
function extractJSON(obj) {
for (const i in obj) {
if (Array.isArray(obj[i]) || typeof obj[i] === 'object') {
extractJSON(obj[i]);
} else {
if(VERBOSE){console.log(i + ': ' + obj[i]);}
//Call next step. Write to file.
eventEmitter.emit('writeToXML',i,obj[i]);
}
}
}
why to parse the entire object?
You should be able to iterate throught the array and emit the events
data.products.results.forEach(result, index => eventEmitter.emit('writeToXML',index,result))
nevertheless the example xml just support one product, is that correct? so each emit creates its own xml file?
You could pass the name as extra argument to your recursive function.
Here is how you could build the XML string with plain JavaScript:
function encodeXML(s) { // Utility to escape reserved characters in XML data
const map = { "<": "<", ">": ">", "&": "&", "'": "&apos", '"': """ };
return String(s).replace(/[<>&'"]/g, m => map[m]);
}
function extractJSON(name, val) {
const content = Array.isArray(val) ? val.map( elem => extractJSON("item", elem) ).join``
: Object(val) === val ? Object.keys(val).map(key => extractJSON(key, val[key]) ).join``
: encodeXML(val);
return `<${name}>${content}</${name}>`;
}
// Example:
var obj = {"data":{"products":{"results":[{"masterData":{"current":{"name":"Paragon Pen","description":"TEST","categories":[{"name":"Writing Instrument"}]}}}]}}};
console.log(extractJSON("data", obj.data));
Note that for arrays, this does not create numerical child tags ( <0>, <1> ), but instead a hard-coded <item> tags, one for each array element.
If you only need the deeply nested part (like in your desired output), then, just pass a reference to that nested object:
function encodeXML(s) { // Utility to escape reserved characters in XML data
const map = { "<": "<", ">": ">", "&": "&", "'": "&apos", '"': """ };
return String(s).replace(/[<>&'"]/g, m => map[m]);
}
function extractJSON(name, val) {
const content = Array.isArray(val) ? val.map( elem => extractJSON("item", elem) ).join``
: Object(val) === val ? Object.keys(val).map(key => extractJSON(key, val[key]) ).join``
: encodeXML(val);
return `<${name}>${content}</${name}>`;
}
// Example:
var obj = {"data":{"products":{"results":[{"masterData":{"current":{"name":"Paragon Pen","description":"TEST","categories":[{"name":"Writing Instrument"}]}}}]}}};
console.log(extractJSON("product", obj.data.products.results[0].masterData.current));
Note that the categories array will be really treated as such, so you still get an <item> level in there.

How to extract the value of an object inside an incomplete JSON array in Javascript?

I have a node app from which I get some data. I have gone through other questions asked on SO..but Im unable to figure this out. I need to access the value of result . The code below shows the exact response I get from server. None of the methods described in other answers like JSON.parse() etc seem to work.
[{
query: {
"parameter1": "12",
"parameter2": "13",
"parameter3": 25
}
result: 6.58443
}]
EDIT : As mentioned in the comments below, unfortunately I cant fix this on the server side(Comes from an external source). I have to deal with this broken JSON on my end and extract the value of result.
EDIT 2 : Yes, there are multiple arrays like this. The content and comma part doesnt change. They are listed one after the other.
Despite the fact you can't receive that data though any library function that expects JSON like jQuery $.ajax() with dataType='json' option (you should use dataType="text" in this case to avoid premature error being triggered I mean).
...you obviously need to fix JSON syntax before parsing it (as I understood you already know).
If it is what you are asking for, your best bet is a regular expression search and replace.
If you know you won't get things such as '{bracket: "}"}' it is pretty simple:
Example:
var wrong = `
[{
"query": {
"parameter1": "12",
"parameter2": "13",
"parameter3": 25
}
"result": 6.58443
}]
`;
var good = wrong.replace(/}(?!\s*[,}\]])/g, '},');
var json = JSON.parse(good);
console.log(json);
This is the simplest example that fixes the input you provided.
Even though it does not fix the same problem after an end of array (']') and, most importantly, if it were fixed it (or simply the string ended with '}' instead of ']' it would added an extra ',' at the end messing up things again.
A more polite approach solving beferementioned issues is to replace the var good = ... row in previous code with this one:
var good = wrong.replace(/(}|])(?!\s*[,}\]])/g, '$1,')
.replace(/,\s*$/, '')
;
Now, you have a valid json object so accessing any property in it is pretty obvious. For example, json[0].result is what you asked for.
On the other hand, if you can have brackets inside literal strings, it will be much more difficult (even not impossible). But I figure out it would hardly be the case...
what you can do is to encapsulate your result with back-ticks to have a (valid) string literal, then get result with any method you want, for example a matching regex :
var arr = `[{
"query": {
"parameter1": "12",
"parameter2": "13",
"parameter3": 25
}
"result": 6.58443
}]`;
var match = arr.match(/("result": )(\d*.\d*)/);
console.log(match[2]);
The suggestions provided above..all point to a hacky way of solving this. The only way to solve this issue was with the help of good old Regex expressions. To my surprise..even though there are lots of libraries to handle JSON parsing etc, to solve edge cases(Common when dealing with small clients or unreliable data source), there is no library which can handle this scenario.
#Bitifet's answer is what solves this problem..with regex.
Purely for illustrative purposes, the below code "rewrites" JSON that is missing commas into JSON that has commas in the appropriate places. The advantage of using this over a replace or a regular expression is that this code guarantees that string literals are handled correctly:
const LEX_EXPR = (
'('
+ '"(?:\\\\(?:["\\\\/bfnrt]|u[a-fA-F0-9]{4})|[^"])*"|'
+ '-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?|'
+ '(?:true|false|null)'
+ ')|'
+ '([{\\[])|'
+ '([}\\]])|'
+ '([:,])|'
+ '(\\s+)|'
+ '(.)'
)
function lex(string) {
let tokens = []
let expr = new RegExp(LEX_EXPR, 'mguy')
let match = expr.exec(string)
while(match !== null) {
let [
value,
atom,
begin, end, sep,
whitespace,
junk
] = match
let type
if (atom != null) {
type = "atom"
} else if (begin != null) {
type = "begin"
} else if (end != null) {
type = "end"
} else if (sep != null) {
type = "sep"
} else if (whitespace != null) {
type = "whitespace"
} else {
// junk. ignore or raise exception
throw `Invalid character: ${junk}`
}
tokens.push({ type, value })
match = expr.exec(string)
}
return tokens
}
function shouldInsertComma(prev, cur) {
if (!prev || !cur) {
return false
}
if (prev.type == "begin" || prev.type == "sep") {
return false
}
return cur.type == "begin" || cur.type == "atom"
}
function rewrite(tokens) {
let out = []
let prevNonWhitespace = null
for (let i = 0; i < tokens.length; i++) {
let cur = tokens[i]
if (cur.type !== "whitespace") {
if (shouldInsertComma(prevNonWhitespace, cur)) {
out.push({ type: "sep", value: "," })
}
prevNonWhitespace = cur
}
out.push(cur)
}
return out
}
function joinTokens(tokens) {
return tokens.map(({ value }) => value).join('')
}
const invalid = `
{
"foo": {
"bat": "bing}"
"boo": "bug"
}
"result": "yes"
}
`
const rewritten = joinTokens(rewrite(lex(invalid)))
console.log(JSON.parse(rewritten)) // { foo: { bat: 'bing}', boo: 'bug' }, result: 'yes' }

different javascript object keys seem to be evaluated as being the same

I'm using an object as a map and storing objects in it using the pattern map[obj.href] = obj
I'm expecting duplicate keys, but something really weird is happening:
I have 2 completely different (every field) objects stored with different keys, but when a 3rd object has the same key as 1st, a lookup is returning the 2nd object (stored with a different key), and when I do a lookup of the returned object's href, I get that same object again, as if the 2 keys are equivalent.
According to Which characters are valid/invalid in a JSON key name? and some other posts on SO, any valid string can be a key in an object, and you don't need to escape any characters, which I was worried about because of all the '/'s and '#'s.
I'm sureit's just a simple bug in my code, which I can't see it because it's 8 am and I have been up all night. Any help spotting it would be appreciated.
function parseSpellsList($)
{
var spellsList = {};
$("ul[class|='link'],ul[class$='level']").each(function() {
var obj, spell_type, links, i, link, a, span, prevObj, hashKey;
obj = {};
links = this.children;
for (i=0; i<links.length; i++) {
link = links[i];
a = link.children[0];
obj.href = a.href.replace("file:///home/ckot/rpg_app/", "").replace("scripts/", "");
obj.name = a.innerHTML.replace("<b>", "").replace("</b>", "");
hashKey = obj.href;
prevObj = null;
if ( !(spellsList.hasOwnProperty(hashKey))) {
// debug code
if ("Blood Blaze" === obj.name || "Vomit Swarm" === obj.name) {
console.log("storing " + JSON.stringify(obj, null, " ") + " with hashKey: " + hashKey);
}
spellsList[hashKey] = obj;
} else {
console.log("\nWARNING: hashKey " + hashKey + " already exists");
prevObj = spellsList[hashKey];
console.log("object we which to store with hashKey: " + hashKey + "\n" + JSON.stringify(obj, null, " ")) ;
console.log("object retrieved with hashKey: " + hashKey + "\n" + JSON.stringify(prevObj, null, " "));
console.log("object retrieved with hashKey: " + prevObj.href + "\n" + JSON.stringify(spellsList[prevObj.href], null, " ") + "\n");
}
}
});
}
when I run it I get the following output:
storing {
"href": "advancedRaceGuide/featuredRaces/orcs.html#blood-blaze",
"name": "Blood Blaze"
} with hashKey: advancedRaceGuide/featuredRaces/orcs.html#blood-blaze
storing {
"href": "advanced/spells/vomitSwarm.html#vomit-swarm",
"name": "Vomit Swarm"
} with hashKey: advanced/spells/vomitSwarm.html#vomit-swarm
WARNING: hashKey advancedRaceGuide/featuredRaces/orcs.html#blood-blaze already exists
object we which to store with hashKey: advancedRaceGuide/featuredRaces/orcs.html#blood-blaze
{
"href": "advancedRaceGuide/featuredRaces/orcs.html#blood-blaze",
"name": "Blood Blaze"
}
object retrieved with hashKey: advancedRaceGuide/featuredRaces/orcs.html#blood-blaze
{
"href": "advanced/spells/vomitSwarm.html#vomit-swarm",
"name": "Vomit Swarm"
}
object retrieved with hashKey: advanced/spells/vomitSwarm.html#vomit-swarm
{
"href": "advanced/spells/vomitSwarm.html#vomit-swarm",
"name": "Vomit Swarm"
}
EDIT:
just for some context I'm using:
node v0.10.22
npm v1.4.24
and the npm modules:
jsdom v1.0.0-pre.3
jquery v2.1.1",
nvm, I was initializing obj at the start of $.each() instead of inside the nested for loop. :(
i guess I just need to get some sleep.

How to write JSON with jQuery?

I have this currencies.json file:
{
"USD": {
"ValueUSD": 325.33,
"ValueEUR": 344.55,
"PreviousValueUSD": 324.55,
"PreviousValueEUR": 354.55,
},
"EUR": {
"ValueUSD": 325.33,
"ValueEUR": 344.55,
"PreviousValueUSD": 324.55,
"PreviousValueEUR": 354.55,
}
}
I need to parse it into "#content" using jQuery. Can someone help me with a code to do this? I think jSONP is needed because the feed is from another server.
Example for output needed:
<div class="currency">USD, 325.33, 344.55, 324.55, 354.55</div>
<div class="currency">EUR, 325.33, 344.55, 324.55, 354.55</div>
// you will get from server
var obj = $.parseJSON(data); // data contains the string
for (var key in obj) {
$('<div class="currency" />')
.html(key + ', ' + $.map(obj[key], function(val) { return val; })
.join(', ')).appendTo('body');
}
HERE is the code.
$.parseJSON is used to parse the string into the object.
Then for each currency inside object use .map() to map the values.
Join the values into a string separated by ,, append into the div and a currency name.
Resulting div append to the body.
Update (see comments):
If you want to retrieve this data cross-domain use:
$.getJSON('www.domain.com/currencies.json?callback=?', function(data) {
for (var key in data) {
$('<div class="currency" />')
.html(key + ', ' + $.map(data[key], function(val) { return val; })
.join(', ')).appendTo('body');
}
});
Something like this should help (the data parsed from your JSON above is held in the data variable):
var $body = $("body"),
key,
$div,
txt,
innerKey;
for (key in data) {
if (data.hasOwnProperty(key)) {
$div= $("<div></div").addClass("currency");
txt = [key, ", "];
for (innerKey in data[key]) {
if (data[key].hasOwnProperty(innerKey)) {
txt.push(data[key][innerKey]);
txt.push(", ");
}
}
// Remove the trailing comma
txt.pop();
// Set the HTML content of the div and then add to the body
$div.html(txt.join("")).appendTo($body);
}
}
Here's a working example jsFiddle.
well you can access things like:
data.USD.ValueUSD will get you 325.33 so you can do something liek this. pass your data object that you get from your ajax call in ur success func to call this function:
function populateContent(data){
var $currencyDiv = $('<div class="currency"></div>'),
$currencyDiv2 = $currencyDiv.clone();
$currencyDiv.html("USD, "+data.USD.ValueUSD + ", " + data.USD.ValueEUR + ", " + data.USD.PreviousValueUSD + ", " + data.USD.PreviousValueEUR);
//do the same for currencydiv2
//append your new content divs wherever you want
$('body').append($currencyDiv);
}
A more puristic approach that could also help you understand how to iterate through objects (and is browser native and therefore not relying on jQuery)
for(var data in #YOUR_JSON_DATA# ){ // iterate through the JSON nodes
var tmp = data; // store the current node in temporary variable
for(var val in json[data]){ // iterate through the current nodes' children
tmp += ", " + json[data][val]; // this is how you access multidimensional objects. format your output as you like
}
alert(tmp); // see the output. here you could use jquery to write this into your page.
}

Categories