Get attribute value from xml by checking conditions - javascript

Following is my xml from where I have to get attribute value:
<R a="1" b="2">
<I attribute1="" attribute2="some text"/>
<I attribute1="" attribute2="some text"/>
<I attribute1="0" attribute2="some text"/>
<I attribute1="0" attribute2="some text"/>
</R>
Here I've to check if attribute1 is not null then I've to get value of attribute2 from I tag.How to do this???
Please help...

UPDATE:
Here's a full X-browser working script that should do the trick. Again, replace the getAttribute('attribute1') by either arguments or return the DOM and take care of the rest. This code might look a bit complicated (it uses closures to be as lightweight as possible) but it should be quite sturdy and safe to use... as long as you don't declare another function called parseXML and you don't call this parseXML prior to it being declared.
var parseXML = (function(w,undefined)
{
'use strict';
var parser,i,ie,parsed;
ie = false;
switch (true)
{
case w.DOMParser !== undefined:
parser = new w.DOMParser();
break;
case new w.ActiveXObject("Microsoft.XMLDOM") !== undefined:
parser = new w.ActiveXObject("Microsoft.XMLDOM");
parser.async = false;
ie = true;
break;
default :
throw new Error('No parser found');
}
return function(xmlString,getTags)
{
var tags,keep = [];
if (ie === true)
{
parser.loadXML(xmlString);
parsed = parser;
}
else
{
parsed = parser.parseFromString(xmlString,'text/xml');
}
tags = parsed.getElementsByTagName(getTags);
for(i=0;i<tags.length;i++)
{
if (tags[i].getAttribute('attribute1') && tags[i].getAttribute('attribute2'))
{
keep.push(tags[i].getAttribute('attribute2'));
}
}
//optional:
keep.push(parsed);//last element of array is the full DOM
return keep;
}
})(this);
var parseResult = parseXML('<r><i attribute1="" attribute2="Ignore This"/><i attribute1="foo" attribute2="got this"/></r>','i');
alert(parseResult[0] || 'nothing');//alerts 'got this' in IE and others
You can parse the XML:
var parser = new DOMParser();
var parsed = parser.parseFromString('<r a="1" b="2"><i v="" x="some text"/><i v="0" x="some important text"/></r>','text/xml');
var iTag = parsed.getElementsByTagName('i');
for (var i=0;i<iTag.length;i++)
{
if (iTag[i].getAttribute('v'))
{
console.log(iTag[i].getAttribute('x'));//do whatever
}
}
This snippet will log some important text, and not some text. That's all there is to it. If you need to store the x values, or return them just declare another variable:
var keep = [];//an array
//change console.log line by:
keep.push(iTag[i].getAttribute('x'));
This is assuming an x property will be set, if that's not always the case, an additional check can easily fix that. The full code will then look like:
function parseXML(xml)
{
'use strict';
var parser,keep,parsed,i,iTag;
parser = new DOMParser();
keep = [];
parsed = parser.parseFromString(xml,'text/xml');//xml is the string
iTag = parsed.getElementsByTagName('i');
for (i=0;i<iTag.length;i++)
{
if (iTag[i].getAttribute('v') && iTag[i].getAttribute('x'))
{
keep.push(iTag[i].getAttribute('x'));
}
}
return keep;//return array
}

Related

Reference/set nested XML object property using path in a string - JavaScript

This is not for web development. I am using ES3.
How do I get the information from the xml element proof using javascript in this scenario?
My way of looking for the proof element with xml[xmlVariable] doesn't work - it returns nothing. But when you enter xml.ait.pages.proof in the console (while the program is held by breakpoint at the return expression) it returns the "desired info" from the proof element correctly.
I've read up on dot/bracket notation thinking that would be the solution but nope.
What's the correct syntax here?
<root>
<ait>
<pages>
<proof>desired info</proof>
</pages>
</ait>
</root>
var xmlFile = "C:\Users\user\Desktop\info.xml"
var xmlElementPath = "ait.pages.proof"
var info = readXMLVar(xmlElementPath, xmlFile)
function readXMLVar(xmlVariable, xmlFilePath) {
var file = new File(xmlFilePath)
file.open("r")
var content = file.read()
file.close()
var xml = new XML(content)
return xml[xmlVariable]
}
For XML I would probably query using XPath. The code you're using, however, seems to create an object structure from the parsed XML, and you then want to ask for a part of that structure using a path to it, as it were.
You can use square bracket notation as you tried, but you have to do it one property/node-level at a time. JS doesn't parse the dot separated path you provided to walk into the nested structure.
As such, you need something that can break apart the path you want, and recursively walk down the structure node by node.
Here is a basic function that can walk an object structure:
var getNodeFromPath = function (data, path, separator) {
var node_name,
node,
ret;
if (!Array.isArray(path)) {
path = path.split(separator || '.');
}
node_name = path.shift();
node = data[node_name];
if (node === undefined) {
ret = null;
} else {
if (path.length) {
ret = getNodeFromPath(node, path);
} else {
ret = node;
}
}
return ret;
};
You could call it like so:
var proof_element = getNodeFromPath(yourParsedXmlData, 'ait.pages.proof');
Note that the function I gave you has minimal control in it. You'll probably want to add some checking to make it more resistant to arbitrary input data/path problems.
Applied fixes to JAAulde's answer and some slight modifications to fit into my function. Here is my code to get and set XML variables.
!(Object.prototype.toString.call(path) === '[object Array]') is used in place of !Array.isArray(path) because I'm forced to use ES3.
function readXMLFile(xmlFilePath) {
var file = new File(xmlFilePath)
file.open("r")
var content = file.read()
file.close()
return [file, new XML(content)]
}
function getXMLVar(xmlFilePath, nodePath, separator) {
var xml = readXMLFile(xmlFilePath)[1]
// navigate xml to return target node info
var getNodeFromPath = function(data, path, separator) {
var node_name,
node,
ret
if(!(Object.prototype.toString.call(path) === '[object Array]')) {
path = path.split(separator || '.')
}
node_name = path.shift()
node = data[node_name]
if(node === undefined) {
ret = null
} else {
if(path.length) {
ret = getNodeFromPath(node, path, separator)
} else {
ret = node
}
}
return ret
}
return getNodeFromPath(xml, nodePath, separator)
}
function setXMLVar(xmlFilePath, nodePath, separator, value) {
var read = readXMLFile(xmlFilePath)
var file = read[0]
var xml = read[1]
setNodeFromPath = function(data, path, separator, value) {
var node_name,
node
if(!(Object.prototype.toString.call(path) === '[object Array]')) {
path = path.split(separator || '.')
}
node_name = path.shift()
node = data[node_name]
if(path.length > 1) {
setNodeFromPath(node, path, separator, value)
} else {
node[path[0]] = value
}
}
setNodeFromPath(xml, nodePath, separator, value)
file.open("w")
file.write(xml)
file.close()
}

How to find the source of a js file from within the js file [duplicate]

I include myscript.js in the file http://site1.com/index.html like this:
<script src=http://site2.com/myscript.js></script>
Inside "myscript.js", I want to get access to the URL "http://site2.com/myscript.js". I'd like to have something like this:
function getScriptURL() {
// something here
return s
}
alert(getScriptURL());
Which would alert "http://site2.com/myscript.js" if called from the index.html mentioned above.
From http://feather.elektrum.org/book/src.html:
var scripts = document.getElementsByTagName('script');
var index = scripts.length - 1;
var myScript = scripts[index];
The variable myScript now has the script dom element. You can get the src url by using myScript.src.
Note that this needs to execute as part of the initial evaluation of the script. If you want to not pollute the Javascript namespace you can do something like:
var getScriptURL = (function() {
var scripts = document.getElementsByTagName('script');
var index = scripts.length - 1;
var myScript = scripts[index];
return function() { return myScript.src; };
})();
You can add id attribute to your script tag (even if it is inside a head tag):
<script id="myscripttag" src="http://site2.com/myscript.js"></script>
and then access to its src as follows:
document.getElementById("myscripttag").src
of course id value should be the same for every document that includes your script, but I don't think it is a big inconvenience for you.
Everything except IE supports
document.currentScript
Simple and straightforward solution that work very well :
If it not IE you can use document.currentScript
For IE you can do document.querySelector('script[src*="myscript.js"]')
so :
function getScriptURL(){
var script = document.currentScript || document.querySelector('script[src*="myscript.js"]')
return script.src
}
update
In a module script, you can use:
import.meta.url
as describe in mdn
I wrote a class to find get the path of scripts that works with delayed loading and async script tags.
I had some template files that were relative to my scripts so instead of hard coding them I made created the class to do create the paths automatically. The full source is here on github.
A while ago I had use arguments.callee to try and do something similar but I recently read on the MDN that it is not allowed in strict mode.
function ScriptPath() {
var scriptPath = '';
try {
//Throw an error to generate a stack trace
throw new Error();
}
catch(e) {
//Split the stack trace into each line
var stackLines = e.stack.split('\n');
var callerIndex = 0;
//Now walk though each line until we find a path reference
for(var i in stackLines){
if(!stackLines[i].match(/http[s]?:\/\//)) continue;
//We skipped all the lines with out an http so we now have a script reference
//This one is the class constructor, the next is the getScriptPath() call
//The one after that is the user code requesting the path info (so offset by 2)
callerIndex = Number(i) + 2;
break;
}
//Now parse the string for each section we want to return
pathParts = stackLines[callerIndex].match(/((http[s]?:\/\/.+\/)([^\/]+\.js)):/);
}
this.fullPath = function() {
return pathParts[1];
};
this.path = function() {
return pathParts[2];
};
this.file = function() {
return pathParts[3];
};
this.fileNoExt = function() {
var parts = this.file().split('.');
parts.length = parts.length != 1 ? parts.length - 1 : 1;
return parts.join('.');
};
}
if you have a chance to use jQuery, the code would look like this:
$('script[src$="/myscript.js"]').attr('src');
Following code lets you find the script element with given name
var scripts = document.getElementsByTagName( 'script' );
var len = scripts.length
for(var i =0; i < len; i++) {
if(scripts[i].src.search("<your JS file name") > 0 && scripts[i].src.lastIndexOf("/") >= 0) {
absoluteAddr = scripts[i].src.substring(0, scripts[i].src.lastIndexOf("/") + 1);
break;
}
}
document.currentScript.src
will return the URL of the current Script URL.
Note: If you have loaded the script with type Module then use
import.meta.url
for more import.meta & currentScript.src
Some necromancy, but here's a function that tries a few methods
function getScriptPath (hint) {
if ( typeof document === "object" &&
typeof document.currentScript === 'object' &&
document.currentScript && // null detect
typeof document.currentScript.src==='string' &&
document.currentScript.src.length > 0) {
return document.currentScript.src;
}
let here = new Error();
if (!here.stack) {
try { throw here;} catch (e) {here=e;}
}
if (here.stack) {
const stacklines = here.stack.split('\n');
console.log("parsing:",stacklines);
let result,ok=false;
stacklines.some(function(line){
if (ok) {
const httpSplit=line.split(':/');
const linetext = httpSplit.length===1?line.split(':')[0]:httpSplit[0]+':/'+( httpSplit.slice(1).join(':/').split(':')[0]);
const chop = linetext.split('at ');
if (chop.length>1) {
result = chop[1];
if ( result[0]!=='<') {
console.log("selected script from stack line:",line);
return true;
}
result=undefined;
}
return false;
}
ok = line.indexOf("getScriptPath")>0;
return false;
});
return result;
}
if ( hint && typeof document === "object") {
const script = document.querySelector('script[src="'+hint+'"]');
return script && script.src && script.src.length && script.src;
}
}
console.log("this script is at:",getScriptPath ())
Can't you use location.href or location.host and then append the script name?

Call functions from sources directly in Chrome console?

For a website there is this function under sources with the code:
betSlipView.prototype.stakeOnKeyUp = function(_key) {
var model = ob.slip.getModel(),
defval = ob.cfg.default_bet_amount;
selector = toJqId(["#stake-", _key].join('')),
stake_box = $(selector),
spl = stake_box.val();
if(spl != defval) {
spl = ob.slip.cleanFormatedAmount(spl);
if(spl === '' || isNaN(spl)) {
spl = 0;
$(selector).val('');
}
model.setBetStake(_key, spl);
$(toJqId(['#ob-slip-estimate-', _key].join(''))).html(
model.getBet(_key, 'pretty_returns')
);
} else {
$(selector).val(defval);
model.setBetStake(_key, defval);
$(toJqId(['#ob-slip-estimate-', _key].join(''))).html(
model.getBet(_key, 'pretty_returns')
);
}
//Update bonus amount
try {
var offers = model.getBet(_key, 'offers');
}
catch(err) {
var offers = "";
}
if(offers !== "" && typeof offers['STLWIN'] !== "undefined") {
this._handleAccumulatorBonusElements(_key, offers['STLWIN']);
};
// potential returns for this bet
this.updateTotals();
};
I cannot figure out how to (if possible) call this function directly from the console. Firstly, when I try to write betSlipView in the console, it cannot be found. Consequently if I copy the code to the console to define the function, betSlipView is still not found and if I try to change the function name, there are some names in the function body that cannot be found either. I wish to call this function with certain arguments, is this possible?
The whole code can be found here https://obstatic1.danskespil.dk/static/compressed/js/ob/slip/crunched.pkg.js?ver=0305f181cb96b61490e0fd2adafa3a91

How to delete an inserted OOXML comment using Word JS API?

I am trying to insert comments inside word document using insertOoxml method. The comment gets inserted successfully.
I want to delete this manually-inserted comment based on one of the user's actions. For example, when they switch from one feature of my add-in to another. I am trying to delete the comment parts from the Ooxml string using regex match and replace for this to work.
Word.run(async(context) => {
let body = context.document.body
let bodyOOXML = body.getOoxml()
await context.sync()
let bodyOOXMLText = bodyOOXML.value
bodyOOXMLText = bodyOOXMLText.replace(/<relationship(.*?)target="comments.xml(.*?)comments">/g, '')
bodyOOXMLText = bodyOOXMLText.replace(/<w:commentRangeStart(.*?)w:commentRangeEnd(.*?)\/>/g, '')
bodyOOXMLText = bodyOOXMLText.replace(/<w:comments(.*?)w:comments>/g, '')
bodyOOXMLText = bodyOOXMLText.replace(/<pkg:part(.*?)comments\+xml(.*?)word\/comments\.xml">(.*?)<\/pkg:part>/g, '')
body.insertOoxml(bodyOOXMLText, Word.InsertLocation.replace)
await context.sync()
})
It throws a GeneralException error. I think I'm corrupting the XML object somewhere, so just wanted to confirm
a. Is this a right approach/workaround to my problem?
b. What am I missing to replace here?
c. Is there any other sophisticated solution possible?
The regex method is not safe. The approach is still the same. Use XML parser and delete the relevant nodes from the XML DOM tree.
Code sample:
export function removeCommentsFromXML(xmlString){
let xmlText = ''
try{
// initialize DOM parser
let parser = new DOMParser()
let namespace = []
// parse XML string into XML DOM object
let xmlDoc = parser.parseFromString(xmlString, 'text/xml')
// get xml namespace prefix for 'pkg'
namespace['pkg'] = xmlDoc.documentElement.getAttribute('xmlns:pkg')
// get all 'pkg:part' nodes
let allChildrenNodes = xmlDoc.getElementsByTagNameNS(namespace['pkg'], 'part')
// delete comments.xml node in pkg:part
let currentChildNode = allChildrenNodes[0]
while (currentChildNode!==null && currentChildNode.getAttribute('pkg:name').match('comments.xml')===null) {
currentChildNode = currentChildNode.nextSibling
}
if(currentChildNode!==null) currentChildNode.parentNode.removeChild(currentChildNode)
// get document relationship package
currentChildNode = allChildrenNodes[0]
while (currentChildNode!==null && currentChildNode.getAttribute('pkg:name').match('word/_rels')===null) {
currentChildNode = currentChildNode.nextSibling
}
// get all relationships
let relationships = currentChildNode.getElementsByTagName('Relationship')
// delete comment relationship from relationships
let currentRelationship = relationships[0]
while (currentRelationship!==null && currentRelationship.getAttribute('Target').match('comments.xml')===null) {
currentRelationship = currentRelationship.nextSibling
}
if(currentRelationship!==null) currentRelationship.parentNode.removeChild(currentRelationship)
// get main document
currentChildNode = allChildrenNodes[0]
while (currentChildNode!==null && currentChildNode.getAttribute('pkg:name').match('/word/document.xml')===null) {
currentChildNode = currentChildNode.nextSibling
}
// get w namespace
namespace['w'] = currentChildNode.childNodes[0].childNodes[0].getAttribute('xmlns:w')
// get commentRangeStart nodes
let commentRangeStartNodes = currentChildNode.getElementsByTagNameNS(namespace['w'], 'commentRangeStart')
while(commentRangeStartNodes.length>0) {
commentRangeStartNodes[0].parentNode.removeChild(commentRangeStartNodes[0])
}
// get commentReference nodes
let commentReferenceNodes = currentChildNode.getElementsByTagNameNS(namespace['w'], 'commentReference')
while(commentReferenceNodes.length>0) {
commentReferenceNodes[0].parentNode.removeChild(commentReferenceNodes[0])
}
// get commentRangeEnd nodes
let commentRangeEndNodes = currentChildNode.getElementsByTagNameNS(namespace['w'], 'commentRangeEnd')
while(commentRangeEndNodes.length>0) {
commentRangeEndNodes[0].parentNode.removeChild(commentRangeEndNodes[0])
}
xmlText = new XMLSerializer().serializeToString(xmlDoc)
}
catch(err){
console.log(err)
}
return xmlText
}
the modified XML string can now be inserted using
body.insertOoxml(xmlText, Word.InsertLocation.replace)

Convert a text from text file to array with fs [node js]

I have a txt file contains:
{"date":"2013/06/26","statement":"insert","nombre":1}
{"date":"2013/06/26","statement":"insert","nombre":1}
{"date":"2013/06/26","statement":"select","nombre":4}
how I can convert the contents of the text file as array such as:
statement = [
{"date":"2013/06/26","statement":"insert","nombre":1},
{"date":"2013/06/26","statement":"insert","nombre":1},
{"date":"2013/06/26","statement":"select","nombre":4}, ];
I use the fs module node js. Thanks
Sorry
I will explain more detailed:
I have an array :
st = [
{"date":"2013/06/26","statement":"insert","nombre":1},
{"date":"2013/06/26","statement":"insert","nombre":5},
{"date":"2013/06/26","statement":"select","nombre":4},
];
if I use this code :
var arr = new LINQ(st)
.OrderBy(function(x) {return x.nombre;})
.Select(function(x) {return x.statement;})
.ToArray();
I get the result I want.
insert select insert
but the problem my data is in a text file.
any suggestion and thanks again.
There is no reason for not to do your file parser yourself. This will work on any size of a file:
var fs = require('fs');
var fileStream = fs.createReadStream('file.txt');
var data = "";
fileStream.on('readable', function() {
//this functions reads chunks of data and emits newLine event when \n is found
data += fileStream.read();
while( data.indexOf('\n') >= 0 ){
fileStream.emit('newLine', data.substring(0,data.indexOf('\n')));
data = data.substring(data.indexOf('\n')+1);
}
});
fileStream.on('end', function() {
//this functions sends to newLine event the last chunk of data and tells it
//that the file has ended
fileStream.emit('newLine', data , true);
});
var statement = [];
fileStream.on('newLine',function(line_of_text, end_of_file){
//this is the code where you handle each line
// line_of_text = string which contains one line
// end_of_file = true if the end of file has been reached
statement.push( JSON.parse(line_of_text) );
if(end_of_file){
console.dir(statement);
//here you have your statement object ready
}
});
If it's a small file, you might get away with something like this:
// specifying the encoding means you don't have to do `.toString()`
var arrayOfThings = fs.readFileSync("./file", "utf8").trim().split(/[\r\n]+/g).map(function(line) {
// this try/catch will make it so we just return null
// for any lines that don't parse successfully, instead
// of throwing an error.
try {
return JSON.parse(line);
} catch (e) {
return null;
}
// this .filter() removes anything that didn't parse correctly
}).filter(function(object) {
return !!object;
});
If it's larger, you might want to consider reading it in line-by-line using any one of the many modules on npm for consuming lines from a stream.
Wanna see how to do it with streams? Let's see how we do it with streams. This isn't a practical example, but it's fun anyway!
var stream = require("stream"),
fs = require("fs");
var LineReader = function LineReader(options) {
options = options || {};
options.objectMode = true;
stream.Transform.call(this, options);
this._buffer = "";
};
LineReader.prototype = Object.create(stream.Transform.prototype, {constructor: {value: LineReader}});
LineReader.prototype._transform = function _transform(input, encoding, done) {
if (Buffer.isBuffer(input)) {
input = input.toString("utf8");
}
this._buffer += input;
var lines = this._buffer.split(/[\r\n]+/);
this._buffer = lines.pop();
for (var i=0;i<lines.length;++i) {
this.push(lines[i]);
}
return done();
};
LineReader.prototype._flush = function _flush(done) {
if (this._buffer.length) {
this.push(this._buffer);
}
return done();
};
var JSONParser = function JSONParser(options) {
options = options || {};
options.objectMode = true;
stream.Transform.call(this, options);
};
JSONParser.prototype = Object.create(stream.Transform.prototype, {constructor: {value: JSONParser}});
JSONParser.prototype._transform = function _transform(input, encoding, done) {
try {
input = JSON.parse(input);
} catch (e) {
return done(e);
}
this.push(input);
return done();
};
var Collector = function Collector(options) {
options = options || {};
options.objectMode = true;
stream.Transform.call(this, options);
this._entries = [];
};
Collector.prototype = Object.create(stream.Transform.prototype, {constructor: {value: Collector}});
Collector.prototype._transform = function _transform(input, encoding, done) {
this._entries.push(input);
return done();
};
Collector.prototype._flush = function _flush(done) {
this.push(this._entries);
return done();
};
fs.createReadStream("./file").pipe(new LineReader()).pipe(new JSONParser()).pipe(new Collector()).on("readable", function() {
var results = this.read();
console.log(results);
});
fs.readFileSync("myfile.txt").toString().split(/[\r\n]/)
This gets your each line as a string
You can then use UnderscoreJS or your own for loop to apply the JSON.parse("your json string") method to each element of the array.
var arr = fs.readFileSync('mytxtfile', 'utf-8').split('\n')
I think this is the simplest way of creating an array from your text file

Categories

Resources