modify xml using javascript - javascript

I am starting to learn xml and I am facing a problem. I need to add elements to the xml file so I use this method:
function newNode()
{
var xmlDoc = loadXMLDoc("Nodes.xml");
var nodes = xmlDoc.documentElement;
var newNode = xmlDoc.createElement('node');
var newName = xmlDoc.createElement('name');
var newText = xmlDoc.createTextNode('Start Node');
newName.appendChild(newText);
newNode.appendChild(newName);
xmlDoc.documentElement.appendChild(newNode);
alert(xmlDoc.getElementsByTagName("name")[0].childNodes[0].nodeValue);
return xmlDoc;
}
it works fine and the alert shows "Start Node", but when I use the same alert command as in:
function z()
{
var xmlDoc = loadXMLDoc("Nodes.xml");
alert(xmlDoc.getElementsByTagName("name")[0].childNodes[0].nodeValue);
}
I get this error: Unable to get value of the property 'childNodes': object is null or undefined.

Could you please send an example of XML file before running your z function ?
With what I can see here, it simply means that there is no "name" node in the document, meaning that getElementsByTagName("name") = [], explaining why getElementsByTagName("name")[0] is undefined.

I hate to be that guy, but XML can be easily modified and traversed using a library like jQuery, dojo, mootools, or my recent favorite zepto (if you don't need to support old IE).

Related

How can I generate a namespace for a KML file in Javascript XPath

I want to preface this by saying that I understand there are a lot of similar questions out there (on stack and other sites) related to this problem, but I have spent the past few hours scouring every nook and cranny of the internet and am still coming up dry.
For reference, I've examined the following sites which have provided some guidance, but generally left me more confused than I was before:
https://humanwhocodes.com/blog/2009/03/24/xpath-in-javascript-part-2/
Javascript Xpath and default namespaces
Give me an example of performing an xpath query on a KML document, from Javascript
XPath and Namespaces
Alright, onto the question. I am using Javascript and XPath to try and read a KML file. I use FileReader to get the contents of the XML file, then use the following code to try and access the XML nodes inside.
const xmlParser = new DOMParser();
const xml = xmlParser.parseFromString(contents,"text/xml"); // Contents variable contains the contents of the file (as read by FileReader)
// Read XML
if (xml.evaluate) {
// Most major browsers (except IE)
var path = "/kml/Document/Folder[1]/Placemark/Point/coordinates";
var evaluator = new XPathEvaluator();
var resolver = evaluator.createNSResolver(xml.documentElement);
var nodes = xml.evaluate(path, xml.documentElement, resolver, XPathResult.ANY_TYPE, null);
var result = nodes.iterateNext();
while (result) {
console.log(result);
result = nodes.iterateNext();
}
}
My KML file begins with:
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>-/XDF</name>
From this I understand that the xmlns property defines the namespace. My Javascript code works as expected when I remove this property from the node (as the file no longer uses a namespace), which leads me to believe that the issue is that I'm not defining a correct namespace for XPath to use.
The resolver variable is something that I copied from one of the sites linked above. I have tried to find a proper reference to how to generate a namespace resolver but cannot find something which works with my KML file.
Is anyone with more experience in this area able to point me in the right direction to generating a namespace object which will allow me to read the KML file?
Thanks to #dandavis for the prompt.
I needed to create a separate function to return the fixed namespace:
function resolver() {
return 'http://www.opengis.net/kml/2.2';
}
and reference my XPath path with the myns: prefix to each node.
var path = "//myns:Document/myns:Folder[1]/myns:Placemark/myns:Point/myns:coordinates";
Below is the amended routine (which references the resolver function above) in case it helps anyone else:
const xmlParser = new DOMParser();
const xml = xmlParser.parseFromString(contents,"text/xml");
// Read XML
if (xml.evaluate) {
// Most major browsers (except IE)
var path = "//myns:Document/myns:Folder[1]/myns:Placemark/myns:Point/myns:coordinates";
var nodes = xml.evaluate(path, xml.documentElement, resolver, XPathResult.ANY_TYPE, null);
var result = nodes.iterateNext();
while (result) {
console.log(result);
result = nodes.iterateNext();
}
}
With XPath 2 and later as supported by Saxon-JS 2 (https://www.saxonica.com/saxon-js/documentation/index.html#!api/xpathEvaluate) you can keep your XPath paths to elements compact and prefix-less by declaring a default namespaces for the XPath evaluation
var path = '//Document/Folder[1]/Placemark/Point/coordinates';
var result = SaxonJS.XPath.evaluate(path, xml, { xpathDefaultNamespace : 'http://www.opengis.net/kml/2.2' });
console.log(result);

How Edit data of an XML node with Javascript

I want to write some data in an existing local XML file with Javascript with some text from an Html page. Is it possible to change content of nodes?
Here is XML sample:
<Notepad>
<Name>Player1</Name>
<Notes>text1</Notes>
</Notepad>
I will get some more text from input and want to add it after "text1", but can't find a solution.
function SaveNotes(content,player)
{
var xml = "serialize.xml";
var xmlTree = parseXml("<Notepad></Notepad>");
var str = xmlTree.createElement("Notes");
$(xmlTree).find("Notepad").find(player).append(str);
$(xmlTree).find("Notes").find(player).append(content);
var xmlString = (new XMLSerializer()).serializeToString(xmlTree);
}
Here is the code to manipulate xml content or xml file :
[Update]
Please check this Fiddle
var parseXml;
parseXml = function(xmlStr) {
return (new window.DOMParser()).parseFromString(xmlStr, "text/xml");
};
var xmlTree = parseXml("<root></root>");
function add_children(child_name, parent_name) {
str = xmlTree.createElement(child_name);
//strXML = parseXml(str);
$(xmlTree).find(parent_name).append(str);
$(xmlTree).find(child_name).append("hello");
var xmlString = (new XMLSerializer()).serializeToString(xmlTree);
alert(xmlString);
}
add_children("apple", "root");
add_children("orange", "root");
add_children("lychee", "root");
you can use it for searching in xml as well as adding new nodes with content in it. (And sorry i dont know how to load xml from client side and display it.)
but this fiddle demo will be helpful in adding content in xml and searching in it.
Hope it helps :)
If you want to achieve this on the client side you can parse your xml into a document object:
See
https://developer.mozilla.org/en-US/docs/Web/Guide/Parsing_and_serializing_XML
and
http://www.w3schools.com/xml/tryit.asp?filename=tryxml_parsertest2
And then manipulate it like you would the DOM of any html doc, e.g. createElement, appendChild etc.
See https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement
Then to serialize it into a String again you could use https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML
Persisting the data
Writing to a local file is not possible in a cross-browser way. In IE you could use ActiveX to read/write file.
You could use cookies to store data on the client side, if your data keeps small enough.
In HTML5 you could use local storage, see http://www.w3schools.com/html/html5_webstorage.asp
Try to use these two package one to convert to json and when is finish the other to come back
https://www.npmjs.com/package/xml2json
https://www.npmjs.com/package/js2xmlparser

how to get xml namespace in javascript

my xml is
<message to="to_test" from="from_test">
<test xmlns="google:mobile:data">
{"message":"test_message"}
</test>
</message>
i get the value {"message":"test_message"} by using the function getChildText("pcm"). But i tried to retrieve the namespace xmlns value.
I tried the following How to get XML namespace? , How to get specific XML namespace in XQuery in SQL Server with no luck, it shows me is not function error what i'm doing wrong?
I forgot to mention, I'm currently staring work with node.js.
update
the above xml output is xmpp stanza.
Here i'm getting the attrs using the following.
stanza.attrs.to gives me to_test.
stanza.attrs.from gives me from_test.
stanza.getChildText("test") gives me {"message":"test_message"}
I tried to get the xmlns using
var parser = new DOMParser();
var documents = parser.parseFromString(stanza, 'text/xml');
var response = documents.responseXML.getElementsByTagName("test");
var sunrise = response[0].getElementsByTagNameNS("[Namespace URI]", "test")[0].getAttribute("xmlns");
console.log(sunrise);
here i got
[xmldom error] element parse error: TypeError: source.indexOf is not a function ##[line:0,col:undefined]
Using the standard browser's DOM parser, you can do the following:
var txt = "<message><test xmlns=\"google:mobile:data\"> {\"message\":\"test_message\"}</test></message>";
parser = new DOMParser();
xmlDoc = parser.parseFromString(txt,"text/xml");
ns = xmlDoc.getElementsByTagName("test")[0].namespaceURI
I have tested with Chrome and IE and it works.
I hit a similar error -- in my case it was because I had passed something besides a string to "parseFromString". Could this be the problem? It looks like "stanza" is not a string.

ActiveXObject FileSystemObject not releasing in Javascript

I have a Javascript function that saves JSON data locally, using an ActiveXObject in IE9. It links into FileSystemObject or FSO scripting for file access.
If this Javascript function is run more than once, I get an error in IE debugger: "SCRIPT70: Permission denied" pointing to ts = savefile.OpenAsTextStream(2);
Why will it run just fine the first time, but not after that? My best guess is that something's not being released properly, although I can find no information on MSDN (or here).
Here's the code:
function SaveMyJSON() {
var ts;
var fso = new ActiveXObject("Scripting.FileSystemObject");
var savefilepath = "C:\\MyFolder\\saveFile.json"
var savefile = fso.GetFile(savefilepath);
// open for writing only, value 2, overwriting the previous
// contents of the file
ts = savefile.OpenAsTextStream(2);
var myTestJson = {"id1" : "one", "id2" : "two"};
// copy to json
ts.WriteLine(myTestJson);
ts.Close;
}
The Close method needs empty parenthesis after it, like so:
ts.Close();
Ref here for more info.

Passing parameters to JavaScript files

Often I will have a JavaScript file that I want to use which requires certain variables be defined in my web page.
So the code is something like this:
<script type="text/javascript" src="file.js"></script>
<script type="text/javascript">
var obj1 = "somevalue";
</script>
But what I want to do is:
<script type="text/javascript"
src="file.js?obj1=somevalue&obj2=someothervalue"></script>
I tried different methods and the best one yet is to parse the query string like this:
var scriptSrc = document.getElementById("myscript").src.toLowerCase();
And then search for my values.
I wonder if there is another way to do this without building a function to parse my string.
Do you all know other methods?
I'd recommend not using global variables if possible. Use a namespace and OOP to pass your arguments through to an object.
This code belongs in file.js:
var MYLIBRARY = MYLIBRARY || (function(){
var _args = {}; // private
return {
init : function(Args) {
_args = Args;
// some other initialising
},
helloWorld : function() {
alert('Hello World! -' + _args[0]);
}
};
}());
And in your html file:
<script type="text/javascript" src="file.js"></script>
<script type="text/javascript">
MYLIBRARY.init(["somevalue", 1, "controlId"]);
MYLIBRARY.helloWorld();
</script>
You can pass parameters with arbitrary attributes. This works in all recent browsers.
<script type="text/javascript" data-my_var_1="some_val_1" data-my_var_2="some_val_2" src="/js/somefile.js"></script>
Inside somefile.js you can get passed variables values this way:
........
var this_js_script = $('script[src*=somefile]'); // or better regexp to get the file name..
var my_var_1 = this_js_script.attr('data-my_var_1');
if (typeof my_var_1 === "undefined" ) {
var my_var_1 = 'some_default_value';
}
alert(my_var_1); // to view the variable value
var my_var_2 = this_js_script.attr('data-my_var_2');
if (typeof my_var_2 === "undefined" ) {
var my_var_2 = 'some_default_value';
}
alert(my_var_2); // to view the variable value
...etc...
Another idea I came across was assigning an id to the <script> element and passing the arguments as data-* attributes. The resulting <script> tag would look something like this:
<script id="helper" data-name="helper" src="helper.js"></script>
The script could then use the id to programmatically locate itself and parse the arguments. Given the previous <script> tag, the name could be retrieved like this:
var name = document.getElementById("helper").getAttribute("data-name");
We get name = helper
Check out this URL. It is working perfectly for the requirement.
http://feather.elektrum.org/book/src.html
Thanks a lot to the author. For quick reference I pasted the main logic below:
var scripts = document.getElementsByTagName('script');
var myScript = scripts[ scripts.length - 1 ];
var queryString = myScript.src.replace(/^[^\?]+\??/,'');
var params = parseQuery( queryString );
function parseQuery ( query ) {
var Params = new Object ();
if ( ! query ) return Params; // return empty object
var Pairs = query.split(/[;&]/);
for ( var i = 0; i < Pairs.length; i++ ) {
var KeyVal = Pairs[i].split('=');
if ( ! KeyVal || KeyVal.length != 2 ) continue;
var key = unescape( KeyVal[0] );
var val = unescape( KeyVal[1] );
val = val.replace(/\+/g, ' ');
Params[key] = val;
}
return Params;
}
You use Global variables :-D.
Like this:
<script type="text/javascript">
var obj1 = "somevalue";
var obj2 = "someothervalue";
</script>
<script type="text/javascript" src="file.js"></script">
The JavaScript code in 'file.js' can access to obj1 and obj2 without problem.
EDIT Just want to add that if 'file.js' wants to check if obj1 and obj2 have even been declared you can use the following function.
function IsDefined($Name) {
return (window[$Name] != undefined);
}
Hope this helps.
Here is a very rushed proof of concept.
I'm sure there are at least 2 places where there can be improvements, and I'm also sure that this would not survive long in the wild. Any feedback to make it more presentable or usable is welcome.
The key is setting an id for your script element. The only catch is that this means you can only call the script once since it looks for that ID to pull the query string. This could be fixed if, instead, the script loops through all query elements to see if any of them point to it, and if so, uses the last instance of such an script element. Anyway, on with the code:
Script being called:
window.onload = function() {
//Notice that both possible parameters are pre-defined.
//Which is probably not required if using proper object notation
//in query string, or if variable-variables are possible in js.
var header;
var text;
//script gets the src attribute based on ID of page's script element:
var requestURL = document.getElementById("myScript").getAttribute("src");
//next use substring() to get querystring part of src
var queryString = requestURL.substring(requestURL.indexOf("?") + 1, requestURL.length);
//Next split the querystring into array
var params = queryString.split("&");
//Next loop through params
for(var i = 0; i < params.length; i++){
var name = params[i].substring(0,params[i].indexOf("="));
var value = params[i].substring(params[i].indexOf("=") + 1, params[i].length);
//Test if value is a number. If not, wrap value with quotes:
if(isNaN(parseInt(value))) {
params[i] = params[i].replace(value, "'" + value + "'");
}
// Finally, use eval to set values of pre-defined variables:
eval(params[i]);
}
//Output to test that it worked:
document.getElementById("docTitle").innerHTML = header;
document.getElementById("docText").innerHTML = text;
};
Script called via following page:
<script id="myScript" type="text/javascript"
src="test.js?header=Test Page&text=This Works"></script>
<h1 id="docTitle"></h1>
<p id="docText"></p>
might be very simple
for example
<script src="js/myscript.js?id=123"></script>
<script>
var queryString = $("script[src*='js/myscript.js']").attr('src').split('?')[1];
</script>
You can then convert query string into json like below
var json = $.parseJSON('{"'
+ queryString.replace(/&/g, '","').replace(/=/g, '":"')
+ '"}');
and then can use like
console.log(json.id);
This can be easily done if you are using some Javascript framework like jQuery.
Like so,
var x = $('script:first').attr('src'); //Fetch the source in the first script tag
var params = x.split('?')[1]; //Get the params
Now you can use these params by splitting as your variable parameters.
The same process can be done without any framework but will take some more lines of code.
Well, you could have the javascript file being built by any of the scripting languages, injecting your variables into the file on every request. You would have to tell your webserver to not dish out js-files statically (using mod_rewrite would suffice).
Be aware though that you lose any caching of these js-files as they are altered constantly.
Bye.
HTML:
<script src='greet.js' data-param1='hello' data-param2='world'></script>
// greet.js:
const prm1=document.currentScript.dataset.param1;
const prm2=document.currentScript.dataset.param2;
Nice question and creative answers but my suggetion is to make your methods paramterized and that should solve all your problems without any tricks.
if you have function:
function A()
{
var val = external_value_from_query_string_or_global_param;
}
you can change this to:
function B(function_param)
{
var val = function_param;
}
I think this is most natural approach, you don't need to crate extra documentation about 'file parameters' and you receive the same. This specially useful if you allow other developers to use your js file.
It's not valid html (I don't think) but it seems to work if you create a custom attribute for the script tag in your webpage:
<script id="myScript" myCustomAttribute="some value" ....>
Then access the custom attribute in the javascript:
var myVar = document.getElementById( "myScript" ).getAttribute( "myCustomAttribute" );
Not sure if this is better or worse than parsing the script source string.
Here i have found an another way of doing this same thing. In the reuired js file [cttricks.js as i have used it for testing, you can have your any .js file], we'll simply list up all script elements and get the required one as it is always going to be at last index. And then get ".attributes.src.value" from that.
Now, in any case of script call, it is
<script src="./cttricks.js?data1=Hello&data2=World"></script>
And in the cttricks.js script file,
var scripts = document.getElementsByTagName('script');
var jsFile = new URL("http://" + scripts[scripts.length-1].attributes.src.value);
/*get value from query parameters*/
console.log(jsFile.searchParams.get("data1"));
console.log(jsFile.searchParams.get("data2"));
Enjoy!!
No, you cant really do this by adding variables to the querystring portion of the JS file URL. If its writing the portion of code to parse the string that bothers you, perhaps another way would be to json encode your variables and put them in something like the rel attribute of the tag? I don't know how valid this is in terms of HTML validation, if thats something you're very worried about. Then you just need to find the rel attribute of the script and then json_decode that.
eg
<script type='text/javascript' src='file.js' rel='{"myvar":"somevalue","anothervar":"anothervalue"}'></script>
If you need a way that passes CSP check (which prohibits unsafe-inline) then you have to use nonce method to add a unique value to both the script and the CSP directive or write your values into the html and read them again.
Nonce method for express.js:
const uuidv4 = require('uuid/v4')
app.use(function (req, res, next) {
res.locals.nonce = uuidv4()
next()
})
app.use(csp({
directives: {
scriptSrc: [
"'self'",
(req, res) => `'nonce-${res.locals.nonce}'` // 'nonce-614d9122-d5b0-4760-aecf-3a5d17cf0ac9'
]
}
}))
app.use(function (req, res) {
res.end(`<script nonce="${res.locals.nonce}">alert(1 + 1);</script>`)
})
or write values to html method. in this case using Jquery:
<div id="account" data-email="{{user.email}}"></div>
...
$(document).ready(() => {
globalThis.EMAIL = $('#account').data('email');
}
Although this question has been asked a while ago, it is still relevant as of today. This is not a trivial approach using script file params, but I already had some extreme use-cases that this way was most suited.
I came across this post to find out a better solution than I wrote a while ago, with hope to find maybe a native feature or something similar.
I will share my solution, up until a better one will be implemented. This works on most modern browsers, maybe even on older ones, didn't try.
All the solutions above, are based on the fact that it has to be injected with predefined and well marked SCRIPT tag and rely completely on the HTML implementation. But, what if the script is injected dynamically, or even worse, what if you are write a library, that will be used in a variety of websites?
In these and some other cases, all the above answers are not sufficient and even becoming too complicated.
First, let's try to understand what do we need to achieve here. All we need to do is to get the URL of the script itself, from there it's a piece of cake.
There is actually a nice trick to get the script URL from the script itself. One of the functionalities of the native Error class, is the ability to provide a stack trace of the "problematic location", including the exact file trace to the last call. In order to achieve this, I will use the stack property of the Error instance, that once created, will give the full stack trace.
Here is how the magic works:
// The pattern to split each row in the stack trace string
const STACK_TRACE_SPLIT_PATTERN = /(?:Error)?\n(?:\s*at\s+)?/;
// For browsers, like Chrome, IE, Edge and more.
const STACK_TRACE_ROW_PATTERN1 = /^.+?\s\((.+?):\d+:\d+\)$/;
// For browsers, like Firefox, Safari, some variants of Chrome and maybe other browsers.
const STACK_TRACE_ROW_PATTERN2 = /^(?:.*?#)?(.*?):\d+(?::\d+)?$/;
const getFileParams = () => {
const stack = new Error().stack;
const row = stack.split(STACK_TRACE_SPLIT_PATTERN, 2)[1];
const [, url] = row.match(STACK_TRACE_ROW_PATTERN1) || row.match(STACK_TRACE_ROW_PATTERN2) || [];
if (!url) {
console.warn("Something went wrong. You should debug it and find out why.");
return;
}
try {
const urlObj = new URL(url);
return urlObj.searchParams; // This feature doesn't exists in IE, in this case you should use urlObj.search and handle the query parsing by yourself.
} catch (e) {
console.warn(`The URL '${url}' is not valid.`);
}
}
Now, in any case of script call, like in the OP case:
<script type="text/javascript" src="file.js?obj1=somevalue&obj2=someothervalue"></script>
In the file.js script, you can now do:
const params = getFileParams();
console.log(params.get('obj2'));
// Prints: someothervalue
This will also work with RequireJS and other dynamically injected file scripts.
I think it is far more better and modern solution to just use localStorage on the page where the javascript is included and then just re-use it inside the javascript itself. Set it in localStorage with:
localStorage.setItem("nameOfVariable", "some text value");
and refer to it inside javascript file like:
localStorage.getItem("nameOfVariable");
It's possible to pass parameters to js modules and read them after via import.meta.url.
For example, with the following HTML
<script type="module">
import './index.mjs?someURLInfo=5';
</script>
the following JavaScript file will log the someURLInfo parameter:
// index.mjs
new URL(import.meta.url).searchParams.get('someURLInfo'); // 5
The same applies when a file imports another:
// index.mjs
import './index2.mjs?someURLInfo=5';
// index2.mjs
new URL(import.meta.url).searchParams.get('someURLInfo'); // 5

Categories

Resources