Keywords: Node.js | File Writing | Object Serialization | fs.writeFileSync | JSON.stringify
Abstract: This article provides an in-depth analysis of the common [object Object] issue when writing objects to files in Node.js. By examining the data type requirements of fs.writeFileSync, it compares different approaches including JSON.stringify, util.inspect, and array join methods, explains the fundamental differences between console.log and file writing operations, and offers comprehensive code examples with best practice recommendations.
Problem Background and Phenomenon Analysis
In Node.js development, developers frequently need to persist data objects to files. A common scenario involves web scraping or data collection programs that need to save structured data locally. However, many developers encounter a confusing issue: when using console.log() to output objects, the content displays correctly, but when using fs.writeFileSync() to write to files, the file content becomes the string [object Object].
Core Problem Analysis
The root cause of this problem lies in how the Node.js file system module handles data types. According to the Node.js official documentation, the data parameter of the fs.writeFileSync(filename, data, [options]) method must be of type String or Buffer. When a JavaScript object is passed, Node.js implicitly calls the object's toString() method for conversion.
The default toString() method of JavaScript objects returns [object Object] (for plain objects) or [object Array] (for arrays). This explains why the original code produces [object Object] output:
// Incorrect example
var obj = {urls: ["https://example.com"]};
fs.writeFileSync("./data.json", obj, "utf-8");
// File content becomes: [object Object]
Fundamental Differences Between console.log and File Writing
Why does console.log() display object content correctly while file writing does not? This is because Node.js's console.log() implementation doesn't simply call the toString() method. Examining the Node.js source code reveals that console.log() internally uses the util.format() method, which intelligently handles various data types, including deep formatting of objects.
This design difference leads to a common misunderstanding in development: developers see correct console output and mistakenly assume objects can be directly used for file writing operations.
Solution Comparison and Analysis
Method 1: JSON Serialization
For scenarios requiring preservation of data structure integrity, JSON.stringify() is the optimal choice. This method converts JavaScript objects to JSON format strings, maintaining complete structural information:
// Basic usage
fs.writeFileSync("./data.json", JSON.stringify(obj), "utf-8");
// Pretty-printed output
fs.writeFileSync("./data.json", JSON.stringify(obj, null, 2), "utf-8");
The second parameter of JSON.stringify() is a replacer function for filtering or transforming values; the third parameter specifies indentation spaces (commonly 2 or 4), making the output JSON more readable.
Method 2: Array Joining
When dealing with simple arrays, the array's join() method can be used to concatenate array elements into a string:
// Array joined as comma-separated string
fs.writeFileSync("./data.json", obj.join(","), "utf-8");
// Output: "https://twitter.com/#!/101Cookbooks,http://www.facebook.com/101cookbooks"
This method is suitable for simple scenarios where array structure preservation isn't necessary, only the array content needs storage.
Method 3: util.inspect
Node.js's util.inspect() method provides more flexible formatting options, particularly suitable for debugging and development phases:
var util = require("util");
fs.writeFileSync("./data.json", util.inspect(obj), "utf-8");
// Output: ["https://twitter.com/#!/101Cookbooks", "http://www.facebook.com/101cookbooks"]
util.inspect() supports various options like depth for controlling recursion depth and colors for adding color markers, but the generated string isn't standard JSON format.
Complete Example Code
Combined with the web scraping scenario from the original problem, here's the complete corrected code example:
const fs = require("fs");
const request = require("request");
const $ = require("cheerio");
// Social media URL regular expressions
const socialurls = [
/https?:\/\/twitter\.com/,
/https?:\/\/www\.facebook\.com/
];
function gotHTML(err, resp, html) {
if (err) {
console.error("Request error:", err);
return [];
}
if (resp.statusCode === 200) {
const parsedHTML = $.load(html);
const social_ids = [];
parsedHTML("a").each(function() {
const href = $(this).attr("href");
if (href) {
for (let i = 0; i < socialurls.length; i++) {
if (socialurls[i].test(href) && !social_ids.includes(href)) {
social_ids.push(href);
break;
}
}
}
});
return social_ids;
}
return [];
}
// Read URL file and process
const urls = fs.readFileSync("./urls.txt", "utf-8").split("\n");
const allResults = [];
urls.forEach(function(line) {
if (line.trim()) {
console.log("Processing URL:", line);
// Simulate asynchronous request
request(line, function(err, resp, html) {
const result = gotHTML(err, resp, html);
if (result.length > 0) {
allResults.push({
url: line,
socialLinks: result
});
// Write to file (using JSON format)
fs.writeFileSync(
"./data.json",
JSON.stringify(allResults, null, 2),
"utf-8"
);
console.log("Found social media links:", result);
}
});
}
});
Best Practice Recommendations
1. Data Type Checking: Always check data types before writing to files, ensuring strings or Buffers are passed.
2. Error Handling: Add appropriate error handling mechanisms, especially since file operations can fail due to permissions, disk space, etc.
3. Asynchronous Considerations: In real scenarios, consider using the asynchronous version fs.writeFile() to avoid blocking the event loop.
4. Format Selection: Choose appropriate formats based on data usage: JSON for data exchange, plain text for logs, binary formats for performance-sensitive scenarios.
5. Encoding Specification: Always explicitly specify file encoding (e.g., "utf-8") to avoid platform-specific issues.
Conclusion
The [object Object] issue when writing objects to files in Node.js is fundamentally a data type conversion problem. Understanding the strict data type requirements of fs.writeFileSync() and the different implementation mechanisms between console.log() and file writing operations is key to avoiding such issues. Selecting appropriate serialization methods (JSON.stringify, util.inspect, or array join) based on specific requirements and following best practices ensures correct data persistence to files.