Reference/set nested XML object property using path in a string - JavaScript - 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()
}

Related

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

Use node module in browser

I'm using the very simple Refify npm module to handle circular structure JSON. It stringifies a circular structure JSON object in Node.js to then send to the client. My Angular frontend receives the stringified JSON and needs to call the parse method of refify to convert it back to a usable object.
How do I include the refify node module in my Angular frontend so I can reference refify?
Backend usage looks like so:
var refify = require("refify");
app.get("/api/entries, function(req, res){
var circularJSON = //a circular JSON object
res.send(refify.stringify(circularJSON));
});
The frontend reference would look like this:
$http.get("/api/entries").success(function(data){
$scope.entries = refify.parse(data);
});
You can either use browserify as a build step, or you could use wzrd.in CDN, which is a CDN for npm modules.
browserify as a build step
Use node-style require() to organize your browser code and load modules installed by npm. Browserify will recursively analyze all the require() calls in your app in order to build a bundle you can serve up to the browser in a single <script> tag. For more information, and examples, click here.
wzrd.in CDN
<script src="https://wzrd.in/standalone/refify#latest"></script>
<script>
window.refify // You can use refify now!
</script>
You can go to https://wzrd.in/standalone/refify#latest, copy the code, and paste it into your own file if you want. See jsfiddle here.
Here is the forked version of Refify you can use in node.js as well as browsers.
Forked Refify
You can simply download the index.js and include it in your AngularJS application. and use it.
see the below code, I have added the whole forked index.js file in snippet and example at the end.
(function(obj) {
if (typeof exports === 'undefined') {
obj.refify = refify;
} else {
module.exports = refify;
}
function refify(obj) {
var objs = [];
var paths = []
var keyStack = [];
var objStack = [];
return walk(obj);
function walk(it) {
if (typeof it !== 'object') {
return it;
}
objs.push(it);
paths.push(keyStack.slice())
objStack.push(it)
var copy = initCopy(it);
for (var k in it) {
keyStack.push(k);
var v = it[k];
var i = objs.indexOf(v);
if (i == -1) {
copy[k] = walk(v)
} else {
var $ref = '#/' + paths[i].join('/');
copy[k] = {
$ref: $ref
};
}
keyStack.pop();
}
objStack.pop();
return copy;
}
}
refify.parse = function(it) {
if (typeof it !== 'object') it = JSON.parse(it);
var keyStack = [];
var copy = initCopy(it);
walk(it);
return copy;
function walk(obj) {
if (typeof obj !== 'object') {
set(copy, keyStack.slice(), obj);
return;
}
for (var k in obj) {
keyStack.push(k);
var current = obj[k];
var objPath = parseRef(current);
while (objPath) {
current = get(copy, objPath);
objPath = parseRef(current);
}
if (current === obj[k]) {
// We did *not* follow a reference
set(copy, keyStack.slice(), initCopy(current));
walk(current);
} else {
// We *did* follow a reference
set(copy, keyStack.slice(), current);
}
keyStack.pop();
}
}
}
refify.stringify = function(obj, replacer, spaces) {
return JSON.stringify(refify(obj), replacer, spaces)
}
function parseRef(value) {
if (typeof value !== 'object') return false;
if (!value.$ref) return false;
var path = value.$ref == '#/' ? [] : value.$ref.split('/').slice(1);
return path
}
function get(obj, path) {
if (!path.length) return obj;
if (typeof obj !== 'object') return;
var next = obj[path.shift()];
return get(next, path);
}
refify.set = set;
function set(obj, path, value) {
if (path.length === 0) throw new Error("Cannot replace root object");
var key = path.shift();
if (!path.length) {
obj[key] = value;
return;
}
switch (typeof obj[key]) {
case 'undefined':
obj[key] = isNaN(parseInt(key, 10)) ? {} : [];
break;
case 'object':
break;
default:
throw new Error("Tried to set property " + key + " of non-object " + obj[key]);
}
set(obj[key], path, value);
}
function initCopy(obj) {
if (typeof obj !== 'object') return obj;
return Array.isArray(obj) ? [] : {}
}
}(this));
// Example with forked version
var obj = {
inside: {
name: 'Stackoverflow',
id: '98776'
}
};
obj.inside.parent = obj;
var refifyObject= refify(obj);
document.getElementById("out").innerHTML = JSON.stringify(refifyObject);
<div id="out"></div>
if you want use module in browser, you can use Commonjs and AMD
for example :
requirejs.org
browserify
commonjs.org
systemjs
you can convert refify to module ( browserify ,requirejs,commonjs,...) and use.
Useful Links :
create module in RequireJS
writing modular js/

emscripten - string/char* parameter reference weirdness

So I'm playing around with emscripten and making linked lists. I started with the following code in C -
struct Node {
struct Node *next;
char *value;
};
extern struct Node *ll_new_node(char *value) {
struct Node *node;
node = malloc(sizeof(struct Node));
node->value = value;
return node;
}
extern struct Node *ll_add(struct Node *root, char *value) {
struct Node *node = ll_new_node(value);
if (root == NULL) {
root = node;
} else {
tail(root)->next = node;
}
return root;
}
//....
In the page I'm calling the code this way -
var new_node = Module.cwrap('ll_new_node', 'object', ['string'])
var add = Module.cwrap('ll_add', 'object', ['object', 'string']);
var list = 0;
Zepto(function($){
var printList = function() {
var node = list;
var s = "head -> ";
while (node != null && node != 0) {
s = s + value(node) + " -> ";
node = next(node);
}
s = s + "NULL";
$('#display').text(s);
};
printList();
$('#addBtn').on('click', function() {
var add_value = $('#itemField').val();
if (add_value && add_value.length > 0) {
list = add(list, add_value);
printList();
}
});
//...
});
What was happening was if I put "A" in the text box and added it to the list, "head -> A -> NULL" would print. Then if I but "B" in the text box and added it to the list, "head -> B -> B -> NULL" would print. The same pattern continued as more nodes were added. It was adding the new node, but it seems all node->value fields pointed to a shared string reference.
I made some test code in C to build and print a list, and it worked as expected with no changes. I ended up fixing it via emscripten by using strcpy on the string passed into ll_new_node and assigning the copy to node->value.
Is this "expected behavior" or a bug? Is the "copied reference" to the string due to some problem with how I'm using it on the JavaScript side? Is there some way to tell Emscripten not to do this?

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

Get attribute value from xml by checking conditions

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
}

Categories

Resources