Jquery parse and save part of XML in localstorage - javascript

I have an XML which I am parsing using jquery, it works but I want to only get part of the XML data and then save all that part in localstorage.
My xml looks like this
<channel id="123"><display-name>123</display-name></name></channel>
<channel id="123"><display-name>123</display-name></name></channel>
<channel id="123"><display-name>123</display-name></name></channel>
<programme id="123"><display-name>123</display-name></name></programme>
<programme id="123"><display-name>123</display-name></name></programme>
<programme id="123"><display-name>123</display-name></name></programme>
But I only want to get all the <programme> data and then save that to localstorage. Im not sure how I can only grab the programme sections.
I have tried saving the whole xml but that didnt seem to output any data. This is what what I have tried.
<div id="text"></div>
$(function(){
$.ajax({
type: "GET",
url: "/myxml",
dataType: "xml",
success: function(xml){
window.localStorage.setItem('fullxml', xml)
$("#text").append(window.localStorage.getItem('fullxml'));
},
error: function() {
alert("An error occurred while processing XML file.");
}
});
});

To get a specific node from an XML document, you can select the XML node like this:
Javascript:
var programmeNodes = fullxml.getElementsByTagName('programme');
jQuery:
var programmeNodes = $(fullxml).find('programme');
My Solution:
This solution grabs all <programme> nodes and saves the data into an array and then stores that array in local storage for later use.
Given XML data like this:
var xml = `
<programmes>
<programme id="1">
<display-name>Test 1</display-name>
</programme>
<programme id="2">
<display-name>Test 2</display-name>
</programme>
</programmes>
`;
Will give an array of objects like this which can then be stored.
[
{
id: 1,
name: Test1
},
{
id: 2,
name: Test2
}
]
Full demo code:
var xml = `
<programmes>
<programme id="1">
<display-name>Test 1</display-name>
</programme>
<programme id="2">
<display-name>Test 2</display-name>
</programme>
</programmes>
`;
var fullxml = $.parseXML(xml);
// find <programme> XML elements
var programmeNodes = $(fullxml).find('programme');
// create array for programme data
var programmesArr = [];
// loop through each programme and store data in array
$.each(programmeNodes, function(i) {
var programmeID = $(programmeNodes[i]).attr('id');
var programmeDisplayName = $(programmeNodes[i]).find('display-name').text();
programmesArr.push({
id: programmeID,
name: programmeDisplayName
});
});
// store programmesArr in local storage
// localStorage only allows strings, so we need to convert array to JSON string
localStorage.setItem('programmes', JSON.stringify(programmesArr));
// get programmes from local storage
var storedProgrammes = JSON.parse(localStorage.getItem('programmes'));
DEMO: https://jsfiddle.net/1nLw7hjr/13/
Usage:
var programmeToFind = 2;
var programme = $.grep(storedProgrammes, function(e) {
return e.id == programmeToFind;
});
console.log(programme[0].id); // 2
console.log(programme[0].name); // Test2
Or as a little function:
function searchProgrammes(id) {
var programme = $.grep(storedProgrammes, function(e) {
return e.id == id;
});
return programme[0];
}
var programme = searchProgrammes(2);
console.log(programme.id); // 2
console.log(programme.name); // Test2
grep()
Finds the elements of an array which satisfy a filter function. The
original array is not affected.

I think you don't get any response because when you append the XML to the document, the browser try to parse it, and because this isn't valid HTML, it fails and show only the text, not the tags. So, the simplest solution will to not use jQuery's append() method, and instead, append the XML via text() (that doesn't parse the HTML tags, and instead escapes them), like:
$(function() {
$.ajax({
type: "GET",
url: "/myxml",
dataType: "xml",
success: function(xml) {
window.localStorage.setItem('fullxml', xml);
var el = $("#text");
el.text(el.text() + window.localStorage.getItem('fullxml'));
},
error: function() {
alert("An error occurred while processing XML file.");
}
});
});
Edit:
If you want to store only some elements, you should convert the XML to objects. Use DOMParser, as follows:
$(function() {
$.ajax({
type: "GET",
url: "/myxml",
dataType: "xml",
success: function(xml) {
var xmldoc;
if (window.DOMParser) {
parser = new DOMParser();
xmldoc = parser.parseFromString(xml, "text/xml");
} else { // Internet Explorer
xmldoc = new ActiveXObject("Microsoft.XMLDOM");
xmldoc.async = false;
xmldoc.loadXML(xml);
}
var programmes = xmldoc.getElementsByTagName('programme');
var str = '';
programmes.forEach(function(el) {
str += el.outerHTML;
});
// str now contains only the <programme> elements
window.localStorage.setItem('fullxml', str);
var el = $("#text");
el.text(el.text() + window.localStorage.getItem('fullxml'));
},
error: function() {
alert("An error occurred while processing XML file.");
}
});
});

Related

How to convert xml into a 2d array in javascript?

I also need to put the array in a variable.
I'm using .DataTable for pagination but it doesn't accept tables that are created from xml using Javascript and according to this https://datatables.net/forums/discussion/2689, I need to convert my xml into 2d array.
Here's my xml file
<person>
<data>
<name>juan</name>
<city>tokyo</city>
<age>20</age>
<sex>m</sex>
</data>
<data>
<name>pedro</name>
<city>manila</city>
<age>22</age>
<sex>m</sex>
</data>
<data>
<name>maria</name>
<city>bangkok</city>
<age>23</age>
<sex>f</sex>
</data>
</person>
My 2D array should look like this:
var person =[
["juan","tokyo","20","m"],
["pedro","manila","22","m"],
["maria","bangkok","23","f"],
];
This is my javascript code. The output shows on my html page but I cannot use it for the DataTable that is why i need to store it in an javascript array. How can I modify this code so i can put it in a variable instead of displaying it in an html page?
function readperson(){
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function(){
if (this.readyState == 4 && this.status ==200){
writeperson(this);
}
}
xmlhttp.open("GET", "person.xml", true);
xmlhttp.send();
}
function writeperson(xml){
var x,i,xmlDoc,txt,ths,trs,tre,the;
xmlDoc = xml.responseXML;
var person =xmlDoc.getElementsByTagName("data");
var l = person.length;
var nodes = person[0].childNodes[0];
//var l3 = nodes[0].length;
var l2 = person[0].childNodes[0].nodeValue;
var arr = [];
//orders.length = 3 since two <data> tag
for(i=0; i < person.length; i++){
//will add brackets inside the array arr
arr.push([]);//example: if arr.push("hello") output is hello,hello,hello
arr[i][0]=person[i].getElementsByTagName("name")[0].childNodes[0].nodeValue
arr[i][1]=person[i].getElementsByTagName("city")[0].childNodes[0].nodeValue
arr[i][2]=person[i].getElementsByTagName("age")[0].childNodes[0].nodeValue
arr[i][3]=person[i].getElementsByTagName("sex")[0].childNodes[0].nodeValue
}
document.getElementById("person").innerHTML = arr;
}
When I use a return statement instead of the innerHTML it does not work.
UPDATE
I figured it out. Here's my final code
$(document).ready(function () {
$.ajax({
type: "GET",
url: "person.xml",
dataType: "xml",
success: function (xml) {
const res = [];
$(xml).find("person > data").each(function (i, person) {
res.push([
$(this).find("name", person).text(),
$(this).find("city", person).text(),
$(this).find("age", person).text(),
$(this).find("sex", person).text(),
]);
});
$("#person_table").DataTable({
data: res,
columns: [
{ title: "Name" },
{ title: "Address" },
{ title: "Age" },
{ title: "Sex." },
],
});
},
});
});
Here is another Vanilla JS take on it with fetch() and DOMParser():
/* uncomment the next line for real application: */
// fetch("person.xml").then(r=>r.text()).then(txt=>{
const atts="name,city,age,sex".split(",");
/* XML data string for SO demo, remove line for real application: */
const txt = `<person><data><name>juan</name><city>tokyo</city><age>20</age><sex>m</sex></data><data><name>pedro</name><city>manila</city><age>22</age><sex>m</sex></data><data><name>maria</name><city>bangkok</city><age>23</age><sex>f</sex></data></person>`;
const xml=new DOMParser().parseFromString(txt,"text/html"),
result=[...xml.querySelectorAll("data")].reduce((res,da)=>
(res.push(atts.map(at=>da.querySelector(at).textContent)),res),
[]);
// Test
console.log(result);
/* end of fetch(), uncomment next line for real application: */
// });
You can use jQuery (you have this tag below your question) to parse html tags. First convert XML data string to DOM HTML, then do all searching and extraction how you regularly would with jQuery:
// XML data string
const xml = `
<person>
<data>
<name>juan</name>
<city>tokyo</city>
<age>20</age>
<sex>m</sex>
</data>
<data>
<name>pedro</name>
<city>manila</city>
<age>22</age>
<sex>m</sex>
</data>
<data>
<name>maria</name>
<city>bangkok</city>
<age>23</age>
<sex>f</sex>
</data>
</person>
`;
// Convert to DOM HTML
const html = $.parseHTML(xml);
// Set array for result
const res = [];
// Parse html, find data tags,
// loop through it's content
$(html).find("data").each(function() {
// For each data element push
// required data to array
res.push([
$(this).find("name").text(),
$(this).find("city").text(),
$(this).find("age").text(),
$(this).find("sex").text()
]);
});
// Test
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Consider the following.
Example: https://jsfiddle.net/Twisty/1vw3z6mf/
JavaScript
$(function() {
function xmlToArray(xml) {
var arr = [];
$(xml).find("person > data").each(function(i, person) {
arr.push([
$("name", person).text(),
$("city", person).text(),
$("age", person).text(),
$("sex", person).text()
]);
});
console.log("Converted", arr);
return arr;
}
function writeperson(xml) {
console.log("Write People");
var people = xmlToArray(xml);
$("#person").html(people);
}
function readperson() {
console.log("Read People");
$.get("person.xml", function(data) {
writeperson(data);
});
}
$("button").click(readperson);
});
jQuery can read XML just like it can reach HTML. So you can use jQuery selectors to traverse the XML. You can do this with .find() or with $("elem", object) shorthand which is the same.
The logic iterates over each data portion and creates Arrays at each index. This gives you an Array of Arrays or a 2D Array that Datatables can use.
I cleaned up other code elements to all use jQuery, yet there is nothing wrong with using JavaScript if you choose.

JavaScript Saving data from an ajax request

Challenge:
While on URL1(random wikipedia page), make an ajax request to URL2(100 most common words wikipedia page), format a list out of the returned data to be used later.
I have to run this from the console while on "URL1"
example:
Navigate to URL1
Open Console
paste code
hit enter
So far I have been able to grab the entire html source while on URL1 with the following:
$.ajax({
url: 'https://en.wikipedia.org/wiki/Most_common_words_in_English',
type: 'GET',
dataType: 'html',
success: function (response) {
console.log(response); // works as expected (returns all html)
}
});
I can see in the console the entire HTML source -- I then went to URL2 to figure out how to grab and format what I needed, which I was able to do with:
var array = $.map($('.wikitable tr'),function(val,i){
var obj = {};
obj[$(val).find('td:first').text()] = $(val).find('td:last').text();
return obj;
});
console.log(JSON.stringify(array));
Now this is where my issue is -- combining the two
$.ajax({
url:'https://en.wikipedia.org/wiki/Most_common_words_in_English',
type:'GET',
dataType:'html',
success: function(data){
// returns correct table data from URL2 while on URL2 -- issue while running from URL1
var array = $.map($('.wikitable tr'),function(val,i){
var obj = {};
obj[$(val).find('td:first').text()] = $(val).find('td:last').text();
return obj;
});
console.log(JSON.stringify(array));
};
});
Im guessing this is due to the HTML I want to map is now a string, and my array is looking for HTML elements on the current page which it of course would not find.
Thanks
Simple fix here! You're exactly right, it's not parsing the html you return, so just tell jQuery to convert it into an object it can use $(data) and use that to find what you need.
In essence, your 'document' now becomes $(data) which you will use as the source of all your queries.
$.ajax({
url: 'https://en.wikipedia.org/wiki/Most_common_words_in_English',
type: 'GET',
dataType: 'html',
success: function(data) {
var myVar = data;
Names = $.map($(myVar).find('.wikitable tr'), function(el, index) {
return $(el).find('td:last').text()
});
console.log(Names);
}
});

Child's child in XML

i have a simple XML file which is loaded on page by a script posted below. It converts from a string to a XML file without any problems, but what complicates everything is the fact, that I can't get to a child's child.
I'd like to know why my code doesn't work and what should I do to get the tag name.
function load_xml() {
$.ajax({
type: "GET",
url: "file.xml",
dataType: "xml",
success: function (xmlData) {
var $first_child = $(xmlData).children()[0];
var first_name = $first_child.nodeName; // returns a proper name of a node
var $second_child = $first_child.children()[0]; // doesn't work
var $second_name = $second_child.nodeName; // returns nothing (don't know why)
},
error: function () {
alert("Could not retrieve XML file.");
}
});
}
In your case $first_child is not a jQuery collection. You need to wrap it with $(). Here is a corrected version.
var first_child = $(xmlData).children()[0]; // [0] actually returns the first "raw" node
var first_name = first_child.nodeName;
var $first_child = $(first_child);
var second_child = $first_child.children()[0];
var second_name = second_child.nodeName;

update xml file using JavaScript and jQuery

I have simple XML file
<?xml version="1.0" encoding="utf-8" ?>
<config>
<device>
<node>1</node>
<name>Deposit</name>
<description>Server</description>
<area>Saloon</area>
<type>Server</type>
</device>
</config>
and i have Ajax which is loading xml:
function changename(node2){
var nodenumber = node2
var newname = "newname"
$.ajax({
type: "GET",
url: "config2.xml",
dataType: "xml",
success: function(xml) {
$(xml).find('device').each(function () {
var node = $(this).find('node');
if (node.text() == nodenumber) {
var name = $(this).find('name').text();
alert(name);
}
});
}
});
}
What i need to do is update name in XML. I need to put newname variable value there.
This code only alert old name. Now i need to update xml file new name which is in newname variable.
Please help
use the same function which allows you to get the old name value:
var xml = $(xml);
xml.find('device').each(function () {
var node = $(this).find('node');
if (node.text() == nodenumber) {
$(this).find('name').text(newname);
}
});
you can also change whole your code in the success callback to this:
var xml = $(xml);
var node = xml.find('device>node')
.filter(function(index, node){ return ($(node).text()==nodenumber); })
.parent().find(">name").text(newname);
the point is it is now changed, but only in what you have in xml variable, and if you create another jQuery object, it has the old value.
Consider that it is a client side change (just in browser) in a xml document which is a JavaScript object, after you made your changes in it you should POST it to your server side, and override the new changed xml over the old version.

jQuery XML: Count number of leaf nodes

How can I count the number of leaf nodes coming off a particular XML node using jQuery?
The XML in question looks similar to this. I want all leaf nodes coming off the <Errors> node.
<Errors>
<ErrorParentCat>
<ErrorTag/>
<ErrorTag2/>
</ErrorParentCat>
</Errors>
In this example I want <ErrorTag/> and <ErrorTag2/> counted only. The result should therefore be 2. Also <ErrorParentCat> is an example tag, there could be many within <Errors> with different names.
Once I have the number I would like a list of these leaf nodes too, if possible.
Assuming you already have an XMLDocument named xml:
var $xml = $(xml),
count = $xml.find('*').filter(function ()
{
return $(this).children().length === 0;
}).length;
console.log(count);
You can also just pass the XML string directly to the jQuery function:
var $xml = $('<Errors><ErrorParentCat><ErrorTag/><ErrorTag2/></ErrorParentCat></Errors>');
// the rest is the same
jsfiddle demo →
Edit you said you wanted a list of those leaf nodes. In the code above, you've already got them:
var $xml = /* whatever */,
$leafNodes = $xml.find('*').filter(function ()
{
return $(this).children().length === 0;
}),
count = $leafNodes.length;
Edit #2 as Tim Down has pointed out (see comments below), you cannot just pass the XML string to $() in IE (!##$ing IE). You should use the jQuery 1.5 function $.parseXML() to parse an arbitrary well-formed XML string into an XMLDocument:
var xmlDoc = $.parseXML('<Errors><ErrorParentCat><ErrorTag/><ErrorTag2/></ErrorParentCat></Errors>'),
$xml = $(xmlDoc);
/* the rest is unchanged */
new jsfiddle demo →
Loads of ways to do this:
var countThem = 0;
jQuery.ajax({
type: "GET",
url: 'blahblah.xml',
dataType: ($.browser.msie) ? "text/xml" : "xml",
success: function(xml) {
var xml2 = load_xml(xml);
$(xml2).find('Errors').each(function(){
$(xml2).find('ErrorParentCat').each(function(){
alert($(this).text()); //alert the contents
countThem++;
});
});
alert(countThem); //alert the total number
}
});
and the XML load function:
function load_xml(msg) {
if ( typeof msg == 'string') {
if (window.DOMParser)//Firefox
{
parser=new DOMParser();
data=parser.parseFromString(text,"text/xml");
}else{ // Internet Explorer
data=new ActiveXObject("Microsoft.XMLDOM");
data.async="false";
data.loadXML(msg);
}
} else {
data = msg;
}
return data;
}

Categories