Javascript Objects to XML String with multiple levels - javascript

I have a multi-dimensional Javascript Object that I can easily convert to a JSON string using a .stringify method. I'm trying to write a function to do something similar but in an XML, markup, format. The catch is I want it to be able to handle any number of dimensions.
Let's say I have the following the multi-dimensional object with values like so:
object['annualrevenues']['option0']['ID'] = 1;
object['annualrevenues']['option0']['text'] = '$50mil';
object['annualrevenues']['option1']['ID'] = 2;
object['annualrevenues']['option1']['text'] = '$100mil';
object['annualrevenues']['option2']['ID'] = 3;
object['annualrevenues']['option2']['text'] = '$200mil';
I want to build a string like so:
var xmlText = <xml><annualrevenues><option0><ID>1</ID><text>$50</text></option0></annualrevenues></xml>
That once returned as a response with contentType 'XMLDOC' will look like this:
<xml>
<annualrevenues>
<option0>
<ID>1</ID>
<text>$50</text>
</option0>
</annualrevenues>
</xml>
So I have the following function:
var xmlText = '<xml>';
xmlText += formatXMLSubObjects(objText,xmlText);
function formatXMLSubObjects(objText,xmlText){
for (prop in objText) {
if (typeof objText[prop] == 'object') {
xmlText += '<'+prop+'>';
for (subProp in objText[prop]) {
if (typeof objText[prop][subProp] == 'object') {
// Run the function again as recursion
xmlText += formatXMLSubObjects(objText[prop][subProp],xmlText);
}
else {
xmlText += '<' + subProp + '>' + objText[prop][subProp] + '</' + subProp + '>';
}
}
xmlText += '</'+prop+'>';
}
else {
xmlText += '<'+prop+'>'+objText[prop]+'</'+prop+'>';
}
}
return xmlText;
}
The problem is when the formatXMLSubObjects function returns from the second call, the original object in the first call has been overwritten and is now undefined.
Anyone able to help with this?

Move the definition of xmlText inside the function and use another variable outside to contain the initial payload, and also local variables in the for loops, otherwise they are considered global and overwritten, and don't pass your xmlText ahead to the call, but simply concatenate the result with the previous one every time.
function formatXMLSubObjects(objText) {
var xmlText = ""; // this will contain only this chunk of the object
for (var prop in objText) {
xmlText += '<'+prop+'>'; // place the tag anyway
if (typeof objText[prop] == 'object') {
xmlText += formatXMLSubObjects(objText[prop]); // if it's an object recurse
} else {
xmlText += objText[prop]; // ... otherwise just add the value (this will work only for simple values
}
xmlText += '</' + prop + '>';
}
return xmlText;
}
var xml = '<xml>';
xml += formatXMLSubObjects(obj);
xml += '</xml>';
Take a look at this fiddle: http://jsfiddle.net/vZjAP/

Related

createHtmlOutputFromFile then append variable as an object from the function

I want to pass all values from Google Sheets to HTML (Google web app) by using Google-Apps-Script doGet() and append in the script to keep as global variable. Faster than html load first then use google.run.script... in my opinion
So getting an object key as a sheet_name and key values as 2D_array data
function getSheetData() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets()
var object = {}
for (var i = 0; i < sheets.length; i++) {
object[sheets[i].getName()] = sheets[i].getDataRange().getValues().filter(r => r[0] !== '')
}
return object
}
in function doGet() I'm using append to add the script to the HTML
function doGet() {
var html = HtmlService.createHtmlOutputFromFile('Main')
html.append('<script> var userData = ' + getSheetData() + ' </script>')
return html
}
Still getting <script> var userData = [object Object] </script>
it should be something like <script> var userData = {key1:[2D Array],key2:[2D Array]} </script>
I also tried with string and it works fine. But can we possibly do as an object?
Thank you in advance
html.append(' var userData = ' + getSheetData() + ' ')
When appending html, the script is appending a string. The object returned by getSheetData() is coerced to a string implicitly, when using the + operator. The coercion doesn't produce a full array as string but just as [object Object].
To bypass this, you may use JSON.stringify() server side:
html.append('<script> var userData = ' + JSON.stringify(getSheetData()) + ' </script>')

javascript .data() cuts the string content by whitespace

So, I have this problem where I have a backwards-button in a webapplication. This is the javascript-code for the button:
function getPrevFunction()
{
localDBSelect("prevViews", function (prevViews)
{
if (prevViews)
{
var prevViewObject = $.parseJSON(prevViews);
var prevViewArray = prevViewObject['funcObjects'];
if (prevViewArray.length > 1)
{
var prevArrayIndex = prevViewArray.length - 2;
var actArrayIndex = prevViewArray.length - 1;
var prevFuncObject = prevViewArray[prevArrayIndex];
var prevFunc = prevFuncObject['function'];
var prevConfig = prevFuncObject['config'];
var inData = prevFuncObject['inData'];
prevViewArray.splice(actArrayIndex, 1);
if (inData !== "")
{
if (prevFunc !== "getGuiSiteList")
{
inData = "<div data-param=" + JSON.stringify(inData) + ">";
}
$('#fieldcontain')[prevFunc](inData, prevConfig);
}
else {
$('#fieldcontain')[prevFunc](prevConfig);
}
if (prevViewArray.length === 1)
{
setVisibilityForBackBtn(false); //If last..
}
prevViewObject['funcObjects'] = prevViewArray;
localDBInsert("prevViews", JSON.stringify(prevViewObject));
}
else {
setVisibilityForBackBtn(false);
}
$('#subcontainer').html("");
if(!$('#fieldcontain').is(":visible"))
{
$('#fieldcontain').show();
}
}
});
}
My problem is that I don't always get the entire content of the json-object. Eg; the json, at the beginning it looks like this:
input = {site: "GAV", location: "EG", set: "INVENTORY", binnum: "B01 T09"}
but after I have tried to fetch the json that is being passed as a data/attribute with an html-element like so:
var input = $(inData).data("param");
the value I recieve looks as follows:
input = "{"site":"GAV","location":"EG","set":"INVENTORY","binnum":"B01"
As you can se it has by some reason cut off all the characters after the whitespace, despite nothing happens between the fact that the last functions is added to the list, and that function is then called again, too be able to go backwards in the application.
I do realize that my explanation is messy and probably hard to understand, but this is the best that I can explain it.
I can provide more code if necessary.
So, I do need the entire json for the getPrevFunction (it is passed as "prevViews")
Use encodeURIComponent() and decodeURIComponent() like below
Setting the data
inData = "<div data-param=" + encodeURIComponent(JSON.stringify(inData)) + ">";
Getting the data
var input = JSON.parse(decodeURIComponent($(testDv).data('param')));
Now there will be no cuttings in the object due to whitespace.

How to check and return data until there are no matches?

I have created a function, that will search for different tags, tags like [image] and [gallery] inside a JSON file. If there is a match, it will return and replace it with a new output. Like an image object or a slideshow.
A JSON object can contain multiple tags of the same type, or contain different ones at the same time. So an object can contain two [image] tags for e.g.
JSON
http://snippi.com/s/bzrx3xi
The problem was, if there are multiple [image] tags found, it was replaced with the same content. I was looking for a script that is searching for the tags, until there will no matches anymore. Many thanks to #debatanu for the solution.
Unfortunately, I have some extra wishes for the script, because now the tags like image, will be replaced for the last image object of the media array inside the JSON and it will only grab and replace the first tag.
I was wondering if it is possible to check each tag and replace each tag with the new output.
Search for tags
filterText: function(data) {
// Loop through JSON data
// Check if [tag] in data.text exists
var dataText = data.text;
var tags = {
"gallery": /\[gallery\]/ig,
"image": /\[image\]/ig
};
if (dataText != undefined) {
var raw_data = dataText;
for (var key in tags) {
if (tags.hasOwnProperty(key)) {
var tag = tags[key];
var tagArr = [];
// Check if [tag] in data.text exists
while ( (tagArr = tag.exec(dataText)) !== null ) {
// Call functions
if (key == "gallery") {
console.error('GALLERY');
parsedHTML = raw_data.replace(tagArr[0], shortcodeController.gallery(data));
raw_data = parsedHTML;
return parsedHTML;
}
if (key == "image") {
console.error('IMAGE');
parsedHTML = raw_data.replace(tagArr[0], shortcodeController.image(data));
raw_data = parsedHTML;
return parsedHTML;
// model.find('p').html(parsedHTML);
}
}
}
}
};
Call the filterText function
getDataPopup: function(data) {
if (data.text != undefined) {
$('.js-popup .article').html(data.text);
var parsed = dataController.filterText(data);
console.log('parsed: ', parsed);
$('.js-popup .article').html(parsed);
} else {
$('.js-popup .article').html(data.intro);
}
},
Above function will search for tags inside a while loop.
This script is called, when the user clicked on an item, that will open a popup screen.
The script that is called by the getDataPopup function when the user clicked on an item, will search for a match, when there is a match found, it will call the function shortcodeController.image(data) that return the new output to the variable: parsedHTML.
The function that generates a new output will look like this:
image: function(data) {
// Development
console.log('shortcodeController.image');
var raw_data = data.text;
var outputHTML;
if (data.media != undefined) {
for (var i = 0; i < data.media.length; i++) {
if (data.media[i].image != undefined) {
outputHTML = '<div class="image">';
// Extract filename
var url = data.media[i].image.src;
var filename = url.substring( url.lastIndexOf('/') + 1, url.lastIndexOf('.') );
// console.log(filename);
outputHTML += '<img src="' + url + '" alt="' + filename + '" />';
//data.media[i].image = undefined;
outputHTML +='</div>';
}
};
return outputHTML;
} else {
// If media doesn't exists return empty string
return '';
}
},
Debatanu has mentioned that I should use data.media[i].image = undefined; directly after the outputHTML that contains the actual image object, but this will result in an undefined error. The first [image] tag is replaced by undefined. When I comment this line out, it will be replaced by the last image object inside the media array, as mentioned before.
Maybe it does not work like properly, because the while loop, however, only looks for the gallery and image tags and if there is a match, running it once, because he already saw the tag. Then it will be called again and replaces the first image tag again with the second image object within the media array. Should there might be a while loop added in the if statements on whether it is a gallery or image, so for each tag within the text object is the function called?
Also I noticed, when I console logged tagArr it will give me an value of null when I past it after the while loop and a empty array [] when I paste it directly after the array is created. Beside that, when I console log tag directly after the while loop is started, it only console log tag once, while there are two image tags set inside the JSON.
You can use exec and loop through it using
var dataText = data.text;
var tags = {
"gallery": /\[gallery\]/ig,
"image": /\[image\]/ig,
};
if (dataText != undefined) {
var raw_data = dataText;
for (var key in tags) {
if (tags.hasOwnProperty(key)) {
var tag = tags[key];
var arr=[];
while ((arr= tag.exec(dataText)) !== null) {
//arr[0] will contain the first match
//var newTag=newTags[key];
//you need to replace the matched output
//so no need for newTag
if (key == "gallery") {
console.error('GALLERY');
//replace the matched output arr[0]
//instead tag or newTag
//Since u have [] to replace we need to ommit the regex literal format /<<exp>>/
parsedHTML = raw_data.replace(arr[0], Triptube.shortcodeController.gallery(data));
//Need to add this line for reflecting the changed data
raw_data=parsedHTML;
model.find('p').html(parsedHTML);
}
if (key == "image") {
console.error('IMAGE');
//replace the matched output arr[0]
//instead tag or newTag
//Since u have [] to replace we need to ommit the regex literal format /<<exp>>/
parsedHTML = raw_data.replace(arr[0], Triptube.shortcodeController.image(data));
console.log(parsedHTML);
//Need to add this line for reflecting the changed data
raw_data=parsedHTML;
model.find('p').html(parsedHTML);
}
}
}
}
}
You can find more about it in MDN
With each loop the exec will give you the next match untill no match is left.
EDIT
I have added the entire filter code from the beginning. You see the raw_data variable should be assigned before the loop. Once that is done, the code below with the image function should give you the proper result.
EDIT 2
First the filterText function will return the parsed html post completion of parsing the html
filterText: function(data) {
// Loop through JSON data
// Check if [tag] in data.text exists
var dataText = data.text;
var tags = {
"gallery": /\[gallery\]/ig,
"image": /\[image\]/ig
};
if (dataText != undefined) {
var raw_data = dataText,
newData=JSON.parse(JSON.stringify(data));//Copy of the data object
for (var key in tags) {
if (tags.hasOwnProperty(key)) {
var tag = tags[key];
var tagArr = [];
// Check if [tag] in data.text exists
while ( (tagArr = tag.exec(dataText)) !== null ) {
// Call functions
if (key == "gallery") {
console.error('GALLERY');
parsedHTML = raw_data.replace(tagArr[0], shortcodeController.gallery(newData));
raw_data = parsedHTML;
//return parsedHTML;
}
if (key == "image") {
console.error('IMAGE');
parsedHTML = raw_data.replace(tagArr[0], shortcodeController.image(newData));
raw_data = parsedHTML;
//return parsedHTML; we will return the parsed HTML only when all the tags have been replaced
// model.find('p').html(parsedHTML);
}
}
}
}
return parsedHTML; //return the parsed HTML here
};
Next is the image function which will parse through the images,
image: function(data) {
// Development
console.log('shortcodeController.image');
var raw_data = data.text;
var outputHTML;
if (data.media != undefined) {
for (var i = 0; i < data.media.length; i++) {
if (data.media[i].image != undefined) {
outputHTML = '<div class="image">';
// Extract filename
var url = data.media[i].image.src;
var filename = url.substring( url.lastIndexOf('/') + 1, url.lastIndexOf('.') );
// console.log(filename);
outputHTML += '<img src="' + url + '" alt="' + filename + '" />';
outputHTML +='</div>';
data.media[i].image = undefined;
//Uncommented the above code, because now the operation will be done on the copy of the original data object
}
};
return outputHTML;
} else {
// If media doesn't exists return empty string
return '';
}
}

Javascript string variable to link

I'm making a chrome extension and I want to convert my javascript string variable into a clickable link. This is what my code does right now,
var regex = /[\w]+[\/]+[\w]+#(?:\d*\.)?\d+/g;
This finds a format on a page e.g. stack/overflow#12453. I convert the regex into a string with this function
function objectToString(object) {
var stringify = "";
for (var property in object) {
stringify += object[property] + '<br>';
}
return stringify;
}
What i want to do is to make that string into a clickable link. So if there are 5 links on a page, each of the strings returned would be a clickable link to a href. Is this possible? I would gladly appreciate any help.
Try this :
var obj = new Object();
obj.link1 = "www.google.com";
obj.link2 = "www.msn.com";
$().ready(function () {
$.each(objectToString(obj).split(','), function (i) {
$("ul").append("<li>" + objectToString(obj).split(',')[i] + "</li>");
});
});
function objectToString(object) {
var stringify = "";
for (var property in object) {
stringify += object[property] + ",";
}
stringify = stringify.slice(0, stringify.length - 1);
return stringify;
}
Jsfiddle : http://jsfiddle.net/u4hghL9c/

JSON to HTML using JSON.parse undefined error

When trying to parse the JSON
[{"title":"First Item","href":"first","children":[{"title":"Sub First Item","href":"sub"}]},{"title":"Second Item","href":"home"}]
into a list for navigation its just returning undefined.
I was using code from another answer which was working fine with hardcoded JSON but when using it from a textbox (as its going to be generated using jquery.nestable.js) it just gived undefined and i cant see why, ive tried escaping the quotation marks too but no luck there.
function convertNav(){
var data = document.getElementById('jsonNav').value;
var jsn = JSON.parse(document.getElementById('jsonNav').value);
var parseJsonAsHTMLTree = function(jsn){
var result = '';
if(jsn.title && jsn.children){
result += '<li>' + jsn.title + '<ul>';
for(var i in jsn.children) {
result += parseJsonAsHTMLTree(jsn.children[i]);
}
result += '</ul></li>';
}
else {
result += '<li>' + jsn.title + '</li>';
}
return result + '';
}
var result = '<ul>'+parseJsonAsHTMLTree(jsn)+'</ul>';
document.getElementById('convertedNav').value = result;
}
Ive put it in a jsfiddle
http://jsfiddle.net/nfdz1jnx/
Your code handles only a single Javascript object but, according to your code, all nodes and sub-nodes are wrapped inside Javascript arrays. You can use Array.prototype.forEach to handle the array objects.
Sample code follows.
function convertNav() {
var data = document.getElementById('seriaNav').value;
var jsn = JSON.parse(document.getElementById('seriaNav').value);
var parseJsonAsHTMLTree = function(jsn) {
var result = '';
jsn.forEach(function(item) {
if (item.title && item.children) {
result += '<li>' + item.title + '<ul>';
result += parseJsonAsHTMLTree(item.children);
result += '</ul></li>';
} else {
result += '<li>' + item.title + '</li>';
}
});
return result + '';
};
var result = '<ul>' + parseJsonAsHTMLTree(jsn) + '</ul>';
document.getElementById('convertedNav').value = result;
}
<textarea class="span7" style="width:400px;height:100px;" id="seriaNav">[{"title":"First Item","href":"first","children":[{"title":"Sub First Item","href":"sub"}]},{"title":"Second Item","href":"home"}]</textarea>
<hr />
<button class="btn btn-primary" onClick="convertNav();return false;">Convert</button>
<hr />
<textarea class="span5" style="width:400px;height:100px;" id="convertedNav"></textarea>
Your jsn variable is an array. You're getting a list of Objects back and you'll need to parse each one.
Add this after you get jsn back and you'll see an example of retrieving your data.
alert(jsn[0].title);
Your JSON is parsed into an array of objects. With this in mind, your paths to extract information are wrong. For example, you have:
if(jsn.title...
...whereas there is no jsn.title. There is, however, json[0].title.
Basically, you're missing a loop, over the objects. After
var result = '';
add
for (var i=0, len=jsn.length; i
...and in code subsequent to that change all references to jsn to jsn[i]
And of course close the loop further down.
(Finally, at the risk of being pedantic, jsn is not the best name for the var; it's not JSON anymore; it used to be, but now it's parsed, so it's an array. JSON is a string.)
[{"title":"First Item","href":"first","children":[{"title":"Sub First Item","href":"sub"}]},{"title":"Second Item","href":"home"}]
This is not JSON, this is an array of objects. You don't need to parse this. It's already parsed. It's a javascript object.
You should be able to just parse it like you would a regular object.
data[0].title
data[0].children[1].title
etc.

Categories