How do I save external JSON data to a JavaScript variable? [duplicate] - javascript

I have this JSON file I generate in the server I want to make accessible on the client as the page is viewable. Basically what I want to achieve is:
I have the following tag declared in my html document:
<script id="test" type="application/json" src="http://myresources/stuf.json">
The file referred in its source has JSON data. As I've seen, data has been downloaded, just like it happens with the scripts.
Now, how do I access it in Javascript? I've tried accessing the script tag, with and without jQuery, using a multitude of methods to try to get my JSON data, but somehow this doesn't work. Getting its innerHTML would have worked had the json data been written inline in the script. Which it wasn't and isn't what I'm trying to achieve.
Remote JSON Request after page loads is also not an option, in case you want to suggest that.

You can't load JSON like that, sorry.
I know you're thinking "why I can't I just use src here? I've seen stuff like this...":
<script id="myJson" type="application/json">
{
name: 'Foo'
}
</script>
<script type="text/javascript">
$(function() {
var x = JSON.parse($('#myJson').html());
alert(x.name); //Foo
});
</script>
... well to put it simply, that was just the script tag being "abused" as a data holder. You can do that with all sorts of data. For example, a lot of templating engines leverage script tags to hold templates.
You have a short list of options to load your JSON from a remote file:
Use $.get('your.json') or some other such AJAX method.
Write a file that sets a global variable to your json. (seems hokey).
Pull it into an invisible iframe, then scrape the contents of that after it's loaded (I call this "1997 mode")
Consult a voodoo priest.
Final point:
Remote JSON Request after page loads is also not an option, in case you want to suggest that.
... that doesn't make sense. The difference between an AJAX request and a request sent by the browser while processing your <script src=""> is essentially nothing. They'll both be doing a GET on the resource. HTTP doesn't care if it's done because of a script tag or an AJAX call, and neither will your server.

Another solution would be to make use of a server-side scripting language and to simply include json-data inline. Here's an example that uses PHP:
<script id="data" type="application/json"><?php include('stuff.json'); ?></script>
<script>
var jsonData = JSON.parse(document.getElementById('data').textContent)
</script>
The above example uses an extra script tag with type application/json. An even simpler solution is to include the JSON directly into the JavaScript:
<script>var jsonData = <?php include('stuff.json');?>;</script>
The advantage of the solution with the extra tag is that JavaScript code and JSON data are kept separated from each other.

It would appear this is not possible, or at least not supported.
From the HTML5 specification:
When used to include data blocks (as opposed to scripts), the data must be embedded inline, the format of the data must be given using the type attribute, the src attribute must not be specified, and the contents of the script element must conform to the requirements defined for the format used.

While it's not currently possible with the script tag, it is possible with an iframe if it's from the same domain.
<iframe
id="mySpecialId"
src="/my/link/to/some.json"
onload="(()=>{if(!window.jsonData){window.jsonData={}}try{window.jsonData[this.id]=JSON.parse(this.contentWindow.document.body.textContent.trim())}catch(e){console.warn(e)}this.remove();})();"
onerror="((err)=>console.warn(err))();"
style="display: none;"
></iframe>
To use the above, simply replace the id and src attribute with what you need. The id (which we'll assume in this situation is equal to mySpecialId) will be used to store the data in window.jsonData["mySpecialId"].
In other words, for every iframe that has an id and uses the onload script will have that data synchronously loaded into the window.jsonData object under the id specified.
I did this for fun and to show that it's "possible' but I do not recommend that it be used.
Here is an alternative that uses a callback instead.
<script>
function someCallback(data){
/** do something with data */
console.log(data);
}
function jsonOnLoad(callback){
const raw = this.contentWindow.document.body.textContent.trim();
try {
const data = JSON.parse(raw);
/** do something with data */
callback(data);
}catch(e){
console.warn(e.message);
}
this.remove();
}
</script>
<!-- I frame with src pointing to json file on server, onload we apply "this" to have the iframe context, display none as we don't want to show the iframe -->
<iframe src="your/link/to/some.json" onload="jsonOnLoad.apply(this, someCallback)" style="display: none;"></iframe>
Tested in chrome and should work in firefox. Unsure about IE or Safari.

I agree with Ben. You cannot load/import the simple JSON file.
But if you absolutely want to do that and have flexibility to update json file, you can
my-json.js
var myJSON = {
id: "12ws",
name: "smith"
}
index.html
<head>
<script src="my-json.js"></script>
</head>
<body onload="document.getElementById('json-holder').innerHTML = JSON.stringify(myJSON);">
<div id="json-holder"></div>
</body>

place something like this in your script file json-content.js
var mainjson = { your json data}
then call it from script tag
<script src="json-content.js"></script>
then you can use it in next script
<script>
console.log(mainjson)
</script>

Check this answer: https://stackoverflow.com/a/7346598/1764509
$.getJSON("test.json", function(json) {
console.log(json); // this will show the info it in firebug console
});

If you need to load JSON from another domain:
http://en.wikipedia.org/wiki/JSONP
However be aware of potential XSSI attacks:
https://www.scip.ch/en/?labs.20160414
If it's the same domain so just use Ajax.

Another alternative to use the exact json within javascript. As it is Javascript Object Notation you can just create your object directly with the json notation. If you store this in a .js file you can use the object in your application. This was a useful option for me when I had some static json data that I wanted to cache in a file separately from the rest of my app.
//Just hard code json directly within JS
//here I create an object CLC that represents the json!
$scope.CLC = {
"ContentLayouts": [
{
"ContentLayoutID": 1,
"ContentLayoutTitle": "Right",
"ContentLayoutImageUrl": "/Wasabi/Common/gfx/layout/right.png",
"ContentLayoutIndex": 0,
"IsDefault": true
},
{
"ContentLayoutID": 2,
"ContentLayoutTitle": "Bottom",
"ContentLayoutImageUrl": "/Wasabi/Common/gfx/layout/bottom.png",
"ContentLayoutIndex": 1,
"IsDefault": false
},
{
"ContentLayoutID": 3,
"ContentLayoutTitle": "Top",
"ContentLayoutImageUrl": "/Wasabi/Common/gfx/layout/top.png",
"ContentLayoutIndex": 2,
"IsDefault": false
}
]
};

While not being supported, there is an common alternative to get json into javascript. You state that "remote json request" it is not an option but you may want to consider it since it may be the best solution there is.
If the src attribute was supported, it would be doing a remote json request, so I don't see why you would want to avoid that while actively seeking to do it in an almost same fashion.
Solution :
<script>
async function loadJson(){
const res = await fetch('content.json');
const json = await res.json();
}
loadJson();
</script>
Advantages
allows caching, make sure your hosting/server sets that up properly
on chrome, after profiling using the performance tab, I noticed that it has the smallest CPU footprint compared to : inline JS, inline JSON, external JS.

Related

JSON.parse reviver to sanitise data (stopping XSS)

I'm trying to reduce vulenrabilities in an old code base, specifically XSS attacks. The general pattern is:
The site is a collection of HTML pages with tags
It uses jQuery
Each page runs its own scripts
Each page's scripts make an $.ajax request to a PHP file which returns text/plain JSON (via json_encode($data))
This data is then used to create dynamic HTML markup to inject into the DOM via either $(selector).html, $(selector).append, or occasionnally document.getElementById(id).innerHTML =
Here's a simplified example of the data flow:
page.html
<head>
<script src="script.js"></script>
</head>
<body>
<table id="myTable"></table>
</body>
script.js
$.ajax({url: "phpFile.php"}, function(data){
data = JSON.parse(data);
$("#myTable tbody").html(data.redcuce(row=>"<tr><td>"+row.text"+</td></tr>", ""))
}
phpFile.php
//database query resulting in $data = [text->'<img src="x" onerror="alert(1)">', text->'Innocent value']
echo json_encode($data);
Some of the data retrieved is user input, so if a field contains <img src="x" onerror="alert(1)">, this executes when rendered in the table.
Solution?
I've passed the below sanitising function to JSON.parse to sanitise the data
const sanitiseJson = (key, value) => typeof value === "string" ? DOMPurify.sanitize(value, { USE_PROFILES: { html: true } }) : value;
This uses the DOMPurify library, which would be very easy for me to implement with search and replace.
As I understand it, this will remove potentially malicious HTML from the JSON object. This will prevent XSS attacks in this scenario.
Is this sufficient for sanitising fetched data that is then inserted into the DOM?
What are the other XSS vulenrabilities that are not addressed by this?
Edit: note I'm well aware that .html() etc is not the proper way to insert data, but this is an old, large code base (45k+ lines) and it's heavily entrenched, so my question is more about if this is an 'acceptable' solution in a pinch or if it doesn't come close
From my limited testing it seems to work, and as I (poorly) understand the way the data is processed I can't see how this would be circumvented easily

Correct parsing of JSON

First of all, forgive me if any of this code is bad, inefficient, or completely wrong, I haven't worked with JSON at all before, or any sort of API work.
So, I'm just trying to create a basic webpage which will display some information from the JSON obtained through JSONP (did I implement it correctly...?). I thought that I was accessing the id element correctly but it seems not as I've tried getting it to show up with alert, console.log, and setting the inner html of the paragraph. Here is the code:
HTML
</head>
<body>
<p id="main">
test
</p>
</body>
<script src="js/parseJSON.js"></script>
<script type="application/json" src="https://www.aviationweather.gov/gis/scripts/MetarJSON.php?taf=true&bbox=-86,41,-82,45&callback=parseJSON"></script>
</html>
Javascript
var parseJSON = function(json) {
var obj = JSON.parse(json);
console.log(obj.features[0].properties.id);
}
This seems like something simple I'm just screwing up. Any help would be appreciated, thanks!
I'm just trying to create a basic webpage which will display some information from the JSON obtained through JSONP (did I implement it correctly...?).
You said type="application/json" so the browser ignored it because it doesn't know how to execute scripts written in JSON.
JSONP is not JSON, it is JavaScript, so the correct Content Type is application/javascript.
Further https://www.aviationweather.gov/gis/scripts/MetarJSON.php?taf=true&bbox=-86,41,-82,45&callback=parseJSON returns JSON not JSONP.
var parseJSON = function(json) {
var obj = JSON.parse(json);
While it is possible for a JSONP service to provide data in the form of a JavaScript string containing JSON, that is never something I've seen. The argument should not be parsed as JSON. It should be a regular JavaScript data structure.
… but first you need the service to return JSONP.

Different types of methods to read JSON

I want to load JSON when the page is loading. I found two methods but what is the difference between following two methods to read JSON.
<script type="text/javascript" src="assets/json/mainpage.json"></script>
//code for what to do with this
another method is
$.getJSON("assets/json/mainpage.json")
You did not find two different methods.
The first version is not how it works.
You can load a JavaScript object that is using object notation using a script tag, so
<script type="text/javascript" src="assets/json/mainpage.js"></script>
could contain
var myJSObject = { "key":"value" };
whereas the second method will call something that returns (static or generated) content like
{ "key":"value" }
For example
$.getJSON("assets/json/mainpage.php");
would call a server process that would do
header("content-type: application/json");
echo '{ "key":"value" }';
If the assets/json/mainpage.json returns a pure JSON, the first method is totally wrong because the tag <script> is intended to serve some sort of scripting language, and a JSON is not a scripting language. It's not impossible to see something like this method, though. If the assets/json/mainpage.json supports JSON padding, mainly knows as JSONP, the following snippet is completely valid:
<script type="text/javascript" src="assets/json/mainpage.json?callback=fn"></script>
You can keep the .json extension (or any extension you like) as long as the server returns the result as a Content-Type: javascript/text on the response header and wraps the result in the callback function. Note that the value of the callback param must be a name of a function that is globally accessible.
The second method is the same as doing an ajax call to the JSON and parsing its content. This is the one you should use, between the two. Although it's kind of useless without a callback or promise for response.
$.getJSON("assets/json/mainpage.json").done(function(res) { /* handle response */ });
Hope it helps.

Extract data from JSON API using Javascript

How do I extract the data below. I only want to print out the value number after "networkdiff" in this API.
This is the URL for the API from a different website:
http://21.luckyminers.com/index.php?page=api&action=getpoolstatus&api_key=8dba7050f9fea1e6a554bbcf4c3de5096795b253b45525c53562b72938771c41
I want the code to automatically retrieve the data from the URL above, and display the value after "networkdiff" to display on my other webpage.
Here's my code so far that I will put in my own webpage:
<HTML>
<body>
<script>
I don't know what I should put in this script part.
</script>
</body>
</html>
Below is the data the URL showed up as:
{
"getpoolstatus":{
"version":"1.0.0",
"runtime":10.684967041016,
"data":{
"pool_name":"21 Coin Pool # Luckyminers.com",
"hashrate":0,
"efficiency":97.79,
"workers":0,
"currentnetworkblock":0,
"nextnetworkblock":1,
"lastblock":40544,
"networkdiff":1,
"esttime":0,
"estshares":4096,
"timesincelast":1240429,
"nethashrate":0
}
}
}
Since the data is coming from an external domain, you can't use Ajax to get the data, unless the server enabled CORS. This doesn't seem to be the case, but it seems to support JSONP:
<script>
function processResponse(data) {
console.log(data);
}
</script>
<script src="http://21.luckyminers.com/index.php?page=api&...&callback=processResponse></script>
The callback=parseResponse makes the server return JS consisting of a function call to processResponse. How to access the information you actually want is explained in Access / process (nested) objects, arrays or JSON.
You need to include JSON.js in your web page to use JSON function in javascript. Here is the URL for download
https://github.com/douglascrockford/JSON-js
And then you can use beloe code to parse the JOSN string into javascript object.
var objectJSON = JSON.parse(jsonStr);
You can alse used stringify fucntion to the viceversa.
In which way you call the JSON?
You can call it with a callback function (working example), including it as a script:
updateResult=function()
{
var s=document.createElement('script');
s.src=domain+"/index.php?page=api&callback=showResult&action=getpoolstatus&api_key="+api_key;
document.body.appendChild(s);
}
You must have the callback defined like:
showResult=function(data)
{
document.getElementById('result').innerText=data.getpoolstatus.data.networkdiff;
}
If you call it with JQuery and get the JSON object, you can define the callback in the argument like the following example, but you must have same-origin (your script must run with the same domain (21.luckyminers.com in this case):
$.getJSON(
domain+"/index.php?page=api&action=getpoolstatus&api_key="+api_key,
function(data)
{
document.getElementById('result').innerText=data.getpoolstatus.data.networkdiff;
}
);
But in any case, be careful. Where did you get the API key? If you put it on a client-side script (like JavaScript) anybody can read the key, and with that key maybe do some damage… :S

Javascript to read file names under a directory

I have placed some XML files under a directory. I need to populate a drop down using javascript to display all those XML file names. How do I do that?
You've been way too broad when it comes to circumstances and situation, so I'll be broad back with an answer.
Given the following:
The web-page with the <select> you need to populate is hosted on the same server as the file list.
The server has the ability to use a server-side language (e.g. PHP, ASP)
You don't mind, or can at least decipher jQuery code (makes what I'm about to post more about the concept than the practice)
You will need something like the following setup:
Create a server-side file that dumps a list of file names
You're going to need to look up some way to retrieve and dump the list of the files. This is so JavaScript & AJAX can go fetch this list and dump in in to the drop-down list. Example output of said script (which I'm aliasing as /server-side-file-list in the JavaScript below)
file-001.xml
file-002.xml
file-003.xml
file-004.xml
Setup the <select> on your page
<!-- Somewhere in the page -->
<select id="xml-file-list" name="xml-file-list"></select>
Setup the JavaScript/Ajax code
<!-- This should go in the <head></head> portion of your page with the select -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
$.ajax({
url: '/server-side-file-list',
success: function(d){
$.each(d.split(/[\r\n]+/),function(i,e){
$('<option />',{ value: e }).text(e).appendTo('#xml-file-list');
});
}
});
</script>
Basic work-flow:
HTML page loads up with an empty <select>
jQuery takes over and fetches a list of files from that /server-side-file-list script using AJAX (behind the scenes)
The results are returned and placed in to the <select> as <option>s.
Done.
--
Food for thought:
A better method may be to load your file list in to the page at run time (if possible). That is to say, if the page you're working on is an ASP or PHP or other type of server-side language page, you can retrieve the file list when the page is called upon and load it at that time (and avoid using javascript altogether).
Assuming you're talking about local files, you need a browser that supports the W3C FileSystem API.
You can test for compatibility at www.html5test.com
If you're completely agnostic about the name of xml file you will need a server-side language to get a list of file of a server directory.
Otherwise, if you're in control of filenames, a workaround could be give them a progressive name (e.g. 1.xml, 2.xml...) and try to make some chained ajax HEAD calls.
If the ajax request of 1.xml returns a 200 server status, then ask for 2.xml and so on, until you get a 404 error. For each ajax call you can add the name to an array and then use it to create a dynamic select.
Of course, for a matter of performance, this should be intended only as a workaround and it's not reliable at all, since a 404 could occur even if the resource exists, so I strongly suggest to use a server side language
anyway this is a jQuery 1.5+ example explaining the workaround. Suppose you have 1.xml, 2.xml and 3.xml. The HTML is just an empty select
<select id="xmlfiles"></select>
Javascript/jQuery
<script>
$(document).ready(function() {
/**
* we need to only know if the resource exists or not
*/
$.ajaxSetup({ type : "head" });
/**
* Function reading xml file. When deferred has been resolved
* (when first 404 occurs) an array of filename is passed.
*/
function getXMLFile(path) {
var deferred = $.Deferred(),
xmlFiles = [];
(function getFile( i ) {
var url = [path, i, '.xml'].join('');
$.when($.ajax(url))
.done(function() {
xmlFiles.push(url);
getFile( ++i );
})
.fail(function() {
deferred.resolve(xmlFiles);
});
}(1));
return deferred.promise();
};
/**
* when we have read xmlfiles in "./" then for each file
* retrieved append an option
*/
$.when(getXMLFile('./')).then(function(file) {
var select = $('#xmlfiles');
/* create options */
$.each(file, function(i, filename) {
var option = $('<option></option>');
option.html(filename).attr('value', filename).appendTo(select)
})
});
});
</script>
and this shows a select with options
./1.xml
./2.xml
./3.xml
(Sorry for my verbosity)

Categories