loading...

How can I read a local JSON file in HTML/JavaScript?

calin92540842 profile image PDS OWNER CALIN (Calin Baenen) ・1 min read

I know, reading files, even locally, if the names are known, isn't really possible in the browser, and for good reason, but I want to get some local JSON w/o drag and drop, or manual input, I'm using the JSON in the game, and I like using separate files (as it's cleaner), and I mean, why wouldn't, or why couldn't the browser let you read local JSON? w/o JS, JSON is nothing.
So, how do I load a JSON file in HTML/JS and save it to a variable?

Thanks!
Cheers!

Discussion

markdown guide
 

Option 1: Use localStorage as a Database

function setData (itemName, itemValue) {
  let databaseString = localStorage.getItem("an-awesome-localstorage-db");
  let databaseObject = databaseString ? JSON.parse(databaseString) : {};
  databaseObject[itemName] = itemValue;
  localStorage.setItem("an-awesome-localstorage-db", JSON.stringify(databaseObject));
  return itemValue;
}

function getData (itemName) {
  let databaseString = localStorage.getItem("an-awesome-localstorage-db");
  let databaseObject = databaseString ? JSON.parse(localStorage.getItem("an-awesome-localstorage-db")) : {};
  return databaseObject[itemName];
}

Use like this:

setData("appState", {
    users: [{name: "James"}, {name: "Mimi"}, {name: "Sam"}]
}); // store data in local database

let appState = getData("appState");

console.log(appState);

Option 2: Use a Low-Code Solution

You can use EasyDB or jsonbox or one of the other many other low code databases.

These wont allow you to have the data be local, but they're simple and easy to use. And, a huge benefit: they make it A LOT easier to share your prototype with other people (all you need is one of these and some static HTML and you have a basic app).

I made a basic queue app that lets you add and remove user names from a list using EasyDB here: github.com/panphora/queue-app

And here's a simpler example:

import easyDB from 'easydb-io';

const db = easyDB({
  database: 'DATABASE_ID',
  token: 'DATABASE_TOKEN'
});

(async() => {
  await db.put('users', [{name: "James"}, {name: "Mimi"}, {name: "Sam"}]);
  let users = await db.get('users');
  console.log({users});
})()

I struggle with this too when all I want to do is get a quick prototype up. These are the best ways I've found so far. Good luck!

 

Oh, I just thought of one more idea!

fswatch -o . | xargs -n1 -I "{}" aws s3 cp data.json s3://your-s3-bucket-name

This will watch the current directory (using fswatch) and then run the aws s3 cp command to upload the data.json file to an s3 bucket called your-s3-bucket-name.

Now you have a single command that will watch your file for changes and keep it in sync with a file at a url (something like "your-s3-bucket-name.s3.amazonaws.c...) that's publicly accessible and can be loaded into any app.

This might seem like a pain to set up (install fswatch, install the aws CLI, configure the aws CLI with your credentials, and then set up an s3 bucket that's public by default), but once it's done, it's a dream to work with!

All of a sudden you have this really easy way to sync local files to remote files whenever they change — and that's incredibly powerful. You can use this with CSS files to update a live website's styles, JS files to change a live prototype's behavior, or just use it to load JSON into a prototype app, like you're doing. It'd definitely a cool thing to have in your tool belt.

P.S. I'm not an expert at fswatch, so don't ask me what that weird | xargs -n1 -I "{}" part of the command does. All I know is it'll run the command after it every time a file in the current directory changes...

 

So you could run a super simple local file server? One of the 1 line things and that will serve folders from your local machine so you can get them with fetch.

npm i -g serve

cd yourFolder

serve

then in your code you can use

   async function getMyFile() {
       let response = await fetch('http://localhost:3000/mydata.json')
       let data = await response.json()
       return data
   }
 

I love your approach, and I know it's almost impossible to do w/o a server/localhost webserver, but it seems a little complicated of an approach, but thank you for your contribution. I will use this if I have something more complicated.

 

OMG, I was just looking into this yesterday and did it by using JQuery:

$(document).ready(function () {
            $.getJSON('MY_JSON_FILE.json', function (data) {
                console.log(data);
            });
        });

Does it without having to run a server of any sort.

 

Also yes, it does require a server(?), for me(?), it provides two (standard) errors:

Access to XMLHttpRequest at 'path.json' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
GET path.json net::ERR_FAILED

how did you get around these? and how can I do it too?

 

Oh, yes regarding CORS policy. Since, I was using Firefox, I had to disable something related to CORS.

My directory structure is as follow:

C:.
│   index.html
│   Synonyms.json
│
├───css
│       bulma.min.css
│
└───js
        jquery-3.5.1.min.js

And, the code itself went in head:

<head>
    <script type="text/javascript" src="js/jquery-3.5.1.min.js"></script>

    <script>
       $(document).ready(function () {
            $.getJSON('Synonyms.json', function (data) {
                console.log(data);
            });
        });
    </script>
</head>

Here is the thing related to CORS on firefox: developer.mozilla.org/en-US/docs/W...

What about other browsers? And how can I ensure, or detect whether a user toggled said specific CORS flag in their browser?
If you could answer these, especially the second one, that would really help me.
Thank you so much!

So, I decided to quickly test it using GitHub pages. Instead of using console.log(data) I got the div and changed the text content:

$('#div_id').text(data.id);

and JSON being:

{
  "id": "Foo"
}

I've only tested it on my current browser (Firefox with CORS thingy off) and my s9+ using bromite (Chromium based) and it seems to be displaying the value of id without any issues.

 

getJSON is using XHR. So is fetch. There must be a server.

 

Wait, did you but this in the head, or body? Or does that not matter?

 

you could just use a database like mongodb stitch wich uses something for documents that is very similar to JSON called BSON (Binary JSON).
JSON

{
"Year":"2020"
}

BSON

{
"Year":"2020"
}

So there are no differences :)
mongodb.com/cloud/stitch

 

The straightforward answer to your question is that you can't (yet). You a few options for storing and loading data from the browser:

  • Server requests -- AJAX requests with fetch or websocket connections for smaller messages and real-time communication
  • Local data storage within the browser -- as of yet there are no standard APIs for local filesystem access (though it's in the works); the two main options are:
    • localStorage -- simple key-value storage that can be used with JSON.stringify() to store objects; can be a little dangerous if you store a ton of data there because it's synchronous and can lock up the main thread, but for smaller objects it's just fine
    • indexedDB -- more like a database, asynchronous so it doesn't lock up the browser, but has a famously clunky API that takes some getting used to; there are Promise-based wrapper libraries that make it much easier though
 

Right now major browsers just do not let you read a local file, therefore using a server to host seems to be the only solution at this time.
However, if you only target Chrome users, native file system, which gives you direct access to your local storage, seems to be a solution as well.

 

Using fetch locally will do to, just pass the file location of the json file and it will work exactly as if you're fetching from an actual api .
async function getData(){
const data = await fetch('data.json'); // assuming that the json file is in the same folder
}// then you can just process it like a normal fetch request

 

TYSM! I appreciate this, and surprisingly it is the method Rishi Kumar Chawda showed (w/o the promises), and as I just asked them, to make this easier, is there a way to let the data break (escape) the scope of the promise?
Thanks!
Cheers!

 

No idea if this works for your use-case, but you could define a JS file which exports a object with the same content. Then you could import it as any other JS-file.

 

I never thought of that, as I primarily use the .json format for it's syntax highlighting, thank you.

 

Depending on your editor and theme, I think you should be able to have nice highlighting in JS files as well. As you said in the end JSON is JS. Hope you can find something which works for you!

 

You shouldn't exclusively use a format because you like it's highlighting, though. JSON has it's use cases, but it's not perfect everywhere ;-;

I know, but I almost need syntax highlighting, even if there's little to highlight, or if there's little differences, it tells me what to do, I haven't stated it yet, but I have a vision impairment, so syntax highlighting is definitely a big thing, especially with smaller, or even sometimes, normally sized text, this is weakened by my (sometimes, now mostly limited) access to my PC which has a big resolution monitor accompanying it.

Sorry I made that so long.
Also, maybe JSON isn't big everywhere, but even big games use it, and look at all the MC map creating community, commands use a JSON format. So really, JSON just needs more love.

But I digress, sorry for making such a long, (and admittedly) tangent.
Have a wonderful day. Cheers!

 

You can use XHR to make a get request, check on google for more details

 

Umm, anything in specific I should look up, like a specific search phrase? Thanks!

 

@stas is referring to making AJAX requests, asynchronous server requests to load files. The modern browser solution is fetch.

This, however, requires a server.

ugh, that sucks....
why does almost EVERYTHING to do with file managing require a server?? There should be a non-server solution, that meets middle-ground for security, like only accessing html / css / js / json... etc... like it is everything, but why?? it makes it so much harder, especially for people that ONLY have access to file:// protocol ;-;
HTML / JS is so brutally strict.

Yeah it's definitely frustrating. There's a lot of historical baggage with file access from the browser though. We've had lots of dangerous stuff happen on the web when Flash and Java applets and such we'rye allowed to access the user's filesystem. They are actually working on it though: web.dev/native-file-system/. There's a ton of edge cases and hard security and API problems involved in it, so progress is slow because they really want to get it right. It's been worked on for 10+ years across several different specs. It's a very hard problem I guess.

 
 

Ok, thank you, is there a way to transport data outside of the walls of the scope of it's promise?
e.g.


let y = null;

Promise {
    // [Promise value scope]
    let x = data;
    y = data; // ? Is this possible, is this allowed?
}

 

You could assign it to a variable outside the scope of the promise. However, you can't be sure if the value is available in the variable when you access it because promise resolution is asynchronous. Hence the code outside the promise which falls after the promise call might run before the promise even resolves and gives you the JSON data.

Could I, theoretically, have a while loop running until the variable is not null? Or is it just a matter of either it works or it doesn't?
Thanks for your help thus far, I really appreciate it!

Of course, you can. But I wouldn't recommend it. It is only good enough to be known that it is possible that way but not enough to use it in practice. You can perhaps leverage async/await to write a somewhat synchronous looking code.

 

If you're using a bundler like Parcel, I believe you can simply use the CommonJS syntax to require("myfile.json") it. Unsure if this is exactly what you're looking for, but hope it helps!

 

No, sorry, but thanks for your contribution, it means a lot!