Page Content

Tutorials

How can I use Node.js to parse JSON?

Understanding JSON

The lightweight data format known as JSON is an acronym for JavaScript Object Notation. Like JavaScript objects, it is usually made up of key-value pairs and functions as a standard format for data sharing. Because it is easier for programs to interpret and transfers data more efficiently than earlier formats like XML, it is widely used in modern web construction. JSON basically enables data to be exchanged or stored as a string of plain text.

Although JSON is based on JavaScript, it is compatible with and understandable by a wide range of programming languages. All property names and string values must be contained in double quotes, which is a crucial component of its structure. The JSON syntax does not allow single quotes. Different data kinds, such as numbers, booleans, arrays, and other objects, are supported by JSON in addition to strings. One well-known NoSQL database, MongoDB, for example, stores records as JSON documents (more precisely, BSON, a binary representation of JSON) by default.

JSON in Node.js

It’s easy to work with JSON in Node.js since JavaScript has built-in ways to convert between JavaScript objects and JSON strings. There are two main methods: JSON.parse() and JSON.stringify().

  • JSON.stringify(): A JavaScript object can be transformed into a JSON string using the JSON.stringify() function. When you need to save JavaScript data to a plain text file or send it over a network, like in an HTTP request, this shift is essential.
  • Take into consideration a JavaScript object that depicts a book:
  • In accordance with the string-like grammar of JSON, JSON.stringify() automatically encloses string values and property names in double quotes.
  • JSON.parse(): JSON.parse() is a function that turns a JSON string back into a JavaScript object by doing the opposite procedure. This is crucial for every Node.js application that gets JSON data from an external API, a file, or a database.
  • How to convert the string back into a JavaScript object from which you can directly access its properties.

Code Example for JSON.stringify():

Let’s imagine a JavaScript object representing a book:

const book = {
  title: 'The Great Gatsby',
  author: 'F. Scott Fitzgerald',
  year: 1925,
  isFiction: true
};
// Convert the JavaScript object into a JSON string
// The 'null, 2' arguments are optional, but make the JSON string
// much more human-readable by adding indentation (2 spaces in this case) [previous turn, 8]
const bookJSON = JSON.stringify(book, null, 2);
console.log('--- Original JavaScript Object ---');
console.log(book);
console.log('\n--- JSON Stringified Version (with pretty-print) ---');
console.log(bookJSON);
console.log('\nType of bookJSON:', typeof bookJSON); // Output: string

Output:

--- Original JavaScript Object ---
{ title: 'The Great Gatsby', author: 'F. Scott Fitzgerald', year: 1925, isFiction: true }
--- JSON Stringified Version (with pretty-print) ---
{
  "title": "The Great Gatsby",
  "author": "F. Scott Fitzgerald",
  "year": 1925,
  "isFiction": true
}
Type of bookJSON: string

Code Example for JSON.parse():

Let’s take the bookJSON string we created earlier and parse it back:

const bookJSON_from_file_or_api = `
{
  "title": "The Great Gatsby",
  "author": "F. Scott Fitzgerald",
  "year": 1925,
  "isFiction": true
}
`; // Imagine this string comes from a file read or an API response [previous turn, 165]
// Convert the JSON string back into a JavaScript object
const bookObject = JSON.parse(bookJSON_from_file_or_api);
console.log('\n--- Parsed JavaScript Object ---');
console.log(bookObject);
console.log('\nType of bookObject:', typeof bookObject); // Output: object
// Now you can directly access its properties
console.log('Book Title:', bookObject.title);         // Output: Book Title: The Great Gatsby
console.log('Book Author:', bookObject.author);       // Output: Book Author: F. Scott Fitzgerald
console.log('Book Year:', bookObject.year);           // Output: Book Year: 1925
console.log('Is Fiction:', bookObject.isFiction);     // Output: Is Fiction: true

Basic Data Storage with JSON and the Module

The File System (fs) module is a crucial component for storing data in a Node.js application, particularly for configuration files or in simpler cases. Reading, writing, and manipulating files on the local operating system is made possible by this integrated Node.js module.

File system operations can be performed synchronously or asynchronously with Node.js. But because they avoid stopping the main thread, asynchronous alternatives are typically chosen by Node.js developers, resulting in more responsive and effective apps. A promise-based API is also available for the fs module in modern Node.js versions (v10 and up), which simplifies asynchronous code by utilising async/await.

By creating a straightforward notes application that saves notes in a JSON file, let’s demonstrate data persistence:

  1. Project Setup: First, we’ll suppose that the project is set up with a file called notes.json will hold the notes for our application.
  2. Reading Existing Notes: The application must read any existing data from notes.json before it may add or edit notes. This will require the creation of a fetchNotes function. This function will retrieve a JavaScript array of notes after reading the file and parsing its contents as JSON. An empty array ought to be returned politely in the event that the file is not there or contains invalid JSON.
  3. Saving Notes: It is necessary to save the updated array back to notes.json after making changes to the notes, such as adding a new one. In order to do this, a saveNotes method will transform the JavaScript array into a JSON string and save it to the file. The desired behaviour for updating the full collection of notes is for fs.writeFile() to overwrite the content of the existing file.
  4. Adding a New Note: The pull, edit, and save cycle is illustrated by the addNote function. Logic is also incorporated to guarantee that note titles are distinct, avoiding duplicates.
  5. Use Case Example You may call these functions from your main application file to use them:

Example:

// Import the promise-based version of the built-in 'fs' (File System) module 
const fs = require('fs').promises;
// Define the path to our data file
const NOTES_FILE = 'notes.json';
/**
 * Fetches notes from the file system.
 * Reads 'notes.json', parses its JSON content, and returns a JavaScript array of notes.
 * If the file doesn't exist or parsing fails, it gracefully returns an empty array.
 * This function handles asynchronous file reading 
 */
const fetchNotes = async () => {
  try {
    // Read the content of the file. 'utf8' encoding ensures it's read as a string 
    const notesString = await fs.readFile(NOTES_FILE, 'utf8');
    // Parse the JSON string back into a JavaScript object (an array in this case) 
    return JSON.parse(notesString);
  } catch (e) {
    // If an error occurs (e.g., file not found, invalid JSON), log it and return an empty array 
    console.error(`Error fetching notes: ${e.message}. Returning empty array.`);
    return [];
  }
};
/**
 * Saves the given notes array to the file system.
 * Converts the JavaScript array to a JSON string and writes it to 'notes.json'.
 * This function will overwrite the existing file content with the updated data 
 * The 'null, 2' arguments in JSON.stringify are for pretty-printing the JSON 
 */
const saveNotes = async (notes) => {
  // Convert the JavaScript notes array into a formatted JSON string 
  // The 'null, 2' arguments ensure human-readable, indented JSON output.
  const notesJSON = JSON.stringify(notes, null, 2);
  // Write the JSON string to the file asynchronously 
  await fs.writeFile(NOTES_FILE, notesJSON);
  console.log('Notes saved successfully!');
};
/**
 * Adds a new note to the collection.
 * It first fetches existing notes, checks for duplicate titles, adds the new note if unique,
 * and then saves the updated collection back to the file.
 *
 * @param {string} title - The title of the new note.
 * @param {string} body - The body/content of the new note.
 */
const addNote = async (title, body) => {
  // Fetch existing notes from the file 
  const notes = await fetchNotes();
  // Check if a note with the same title already exists.
  // The 'filter' method creates a new array with all elements that pass the test 
  const duplicateNotes = notes.filter((note) => note.title === title);
  // If no duplicate titles are found (i.e., duplicateNotes array is empty)
  if (duplicateNotes.length === 0) {
    // Add the new note object to the notes array. 'push' adds to the end of an array 
    notes.push({ title, body });
    // Save the modified notes array back to the file system 
    await saveNotes(notes);
    console.log(`Note "${title}" created successfully.`);
  } else {
    // Inform the user if the title is not unique 
    console.log(`Error: Note with title "${title}" already exists. Please use a unique title.`);
  }
};
/**
 * Lists all notes stored in the system.
 */
const listNotes = async () => {
  const notes = await fetchNotes();
  if (notes.length > 0) {
    console.log('\n--- Your Notes ---');
    // Iterate over the notes array and log each note's details.
    notes.forEach((note, index) => { // 'forEach' is an array method 
      // Template literals (ES6 feature) make string concatenation easier .
      console.log(`${index + 1}. Title: ${note.title}`);
      console.log(`   Body: ${note.body}`);
    });
    console.log('------------------\n');
  } else {
    console.log('No notes found. Add some notes to get started!');
  }
};
/**
 * Removes a note by its title.
 * @param {string} title - The title of the note to remove.
 */
const removeNote = async (title) => {
  let notes = await fetchNotes();
  // Filter out the note to be removed.
  // This creates a new array containing only notes whose title does NOT match the one to be removed .
  const notesToKeep = notes.filter((note) => note.title !== title);
  if (notes.length > notesToKeep.length) { // Checks if any note was actually removed
    await saveNotes(notesToKeep);
    console.log(`Note "${title}" removed successfully.`);
  } else {
    console.log(`Note with title "${title}" not found.`);
  }
};
// --- Example Usage ---
// This immediately invoked async function (IIAF) runs our example operations.
(async () => {
  console.log('--- Starting Notes Application Example ---');
  // Add some notes
  await addNote('My First Note', 'This is the content of my first note.');
  await addNote('Shopping List', 'Milk, Eggs, Bread, Cheese.');
  await addNote('My First Note', 'This is a duplicate title, should fail.'); // This will demonstrate the duplicate check
  // List all notes
  await listNotes();
  // Remove a note
  await removeNote('Shopping List');
  await removeNote('Non-Existent Note'); // This will demonstrate not found scenario
  // List notes again to see changes
  await listNotes();
  console.log('--- Notes Application Example Finished ---');
})();

With this method, a JSON file is treated as a simple database, enabling a Node.js application to store data. Data is stringified and written back when it is altered, and it is read and parsed when it is required. Prior to adding more intricate database connectors, this pattern is essential to comprehending data flow and durability in Node.js.

Like a conscientious librarian, consider data permanence with JSON and fs. The global language used to write all books (your data) is JSON, which makes sure that anyone can interpret them. A JavaScript object is meticulously packaged into a nicely formatted book for storage by JSON.stringify(), and it is unpacked and made readable and useable once more by JSON.parse(). It is the librarian’s responsibility to locate, read, and write these volumes to and from the shelves (your file system) in an efficient manner. When combined, they give your program the ability to store and retrieve data, guaranteeing that your data is preserved for further use.

Index