In my previous post, I reviewed the different web APIs for working with files. In this post, we will put that knowledge to use and make a simple JSON file validator web app.
A user can go to our app, upload their JSON file, and it will tell them if it is valid or not, all from the browser. No dependencies, no call to external APIs. Just pure JavaScript and HTML.
First the boilerplate HTML, CSS, and element selectors.
<div class="container">
<h1><code>JSON</code> validator</h1>
<input name="file" accept=".json" type="file" id="file-input" />
<div id="message" />
</div>
The attribute
type="file"
tells the browser to render a file selector instead of a normal text input.accept=".json"
tells the browser to only allow the user to select JSON files.
<style>
.container {
padding-top: 10px;
max-width: 500px;
margin: auto;
display: flex;
flex-direction: column;
}
h1 {
font-size: 30px;
}
code {
border-radius: 3px;
background-color: gainsboro;
padding: 4px;
font-size: 0.9em;
}
#message {
padding-top: 20px;
font-size: 20px;
}
</style>
<script type="module">
const input = document.getElementById("file-input");
const message = document.getElementById("message");
</script>
To start, add an event listener to the input's change
event. The callback will receive an event with the <input>
as its target. The input the user selects is accessible via the files
attribute, which is a list of Files
. Since our input only takes one file, we will take the file at index 0 of the list.
addEventListener("change", (e) => {
const file = e.target.files[0];
});
Next, we need to get the text in the file. This is where the magic of web APIs comes in. A File
is a wrapper around another web API, Blob
(funny name, I know. It actually is shorthand for binary large object). So anything a Blob
can do, a File
can do, which is handy because Blob
has a method, .text()
, which returns the file content as text. Perfect.
.text()
returns a promise, so we will make our function async and await the result.
addEventListener("change", async (e) => {
const file = e.target.files[0];
const text = await file.text();
});
With the text of the file in hand, we can do our JSON validation with yet another web API available in all browsers, JSON.parse()
. This method takes a string as its argument and converts it to a JavaScript object if it is valid JSON or it will throw an error if it's not valid JSON. So all the hard work of parsing and checking the string is done for us by the browser!
Since there is the potential of an error being thrown, we need to wrap the function call in a try...catch block.
input.addEventListener("change", async (e) => {
const file = e.target.files[0];
const text = await file.text();
try {
JSON.parse(text);
// Do something if it works
} catch (e) {
// Do something if it fails
}
});
If the JSON file is invalid, no code below JSON.parse(text)
and above catch
will run. Instead, it will jump directly to the code inside the catch
block.
This means we can put the code to handle valid JSON under JSON.parse(text)
and the code to handle invalid JSON under catch
.
To alert the user if their JSON file is valid, we will append HTML inside of the <div id="message" />
.
The error thrown by .parse()
should look something like JSON.parse: (some generic error message) at line 2 column 3 of the JSON data
. By splitting the error message string on the word at
we can get the location of the error in the JSON file to display to the user.
input.addEventListener("change", async (e) => {
const file = e.target.files[0];
const text = await file.text();
try {
JSON.parse(text);
message.innerHTML = `<div>Valid <code>JSON</code> ✅<div/>`;
} catch (e) {
message.innerHTML = `<div>Invalid <code>JSON</code> ❌ at ${e.message.split(" at ")[1]}<div/>`;
}
});
And there you have it. A fully functional JSON validator. Here is the full HTML file you can copy and play around with and a live demo to prove that it works!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JSON Validator</title>
<script type="module">
const input = document.getElementById("file-input");
const message = document.getElementById("message");
input.addEventListener("change", async (e) => {
const file = e.target.files[0];
const text = await file.text();
try {
JSON.parse(text);
message.innerHTML = `<div>Valid <code>JSON</code> ✅<div/>`;
} catch (e) {
message.innerHTML = `<div>Invalid <code>JSON</code> ❌ at ${e.message.split(" at ")[1]}<div/>`;
}
});
</script>
<style>
body {
font-size: 16px;
}
.container {
padding-top: 10px;
max-width: 500px;
margin: auto;
display: flex;
flex-direction: column;
}
h1 {
font-size: 30px;
}
code {
border-radius: 3px;
background-color: gainsboro;
padding: 4px;
font-size: 0.9em;
}
#message {
padding-top: 20px;
font-size: 20px;
font-weight: 500;
}
</style>
</head>
<body>
<div class="container">
<h1><code>JSON</code> validator</h1>
<input name="file" accept=".json" type="file" id="file-input" />
<div id="message" class="message" />
</div>
</body>
</html>
Top comments (3)
Now show me the pointer to where exactly my JSON is wrong 😏😉
Good idea! I updated the code example and demo site to show a way that could be done using the error message returned by
.parse()
, which will contain the location of the error in the JSON.