JSON.stringify(text) returns text as a JSON string, quotes included, with every character that needs it escaped. In Python, json.dumps(text) does the same.
const text = 'She said "hi"\nC:\\temp';
console.log(JSON.stringify(text));
console.log(JSON.stringify({ note: text }));
"She said \"hi\"\nC:\\temp"
{"note":"She said \"hi\"\nC:\\temp"}
The tool below escapes the same two lines to She said \"hi\"\nC:\\temp, the inside of the string without its outer quotes.
What must be escaped
RFC 8259, section 7, requires three things to be escaped in a JSON string: the quotation mark, the backslash, and the control characters U+0000 to U+001F. Five of those controls have short forms, \b, \f, \n, \r and \t, and the rest are written like \u0001. Everything else may appear as itself: /, ', DEL and any non-ASCII character. \/ is allowed but never needed, and \' isn’t JSON at all: JSON.parse rejects it with Bad escaped character, and Python’s json.loads with Invalid \escape.
The replace trap
Escaping only the quotes looks right and isn’t:
const path = 'C:\\temp\\new';
const naive = '"' + path.replaceAll('"', '\\"') + '"';
console.log(naive);
console.log(JSON.parse(naive).split(/\s/));
console.log(JSON.stringify(path));
"C:\temp\new"
[ 'C:', 'emp', 'ew' ]
"C:\\temp\\new"
The backslashes were left alone, so the parser read \t as a tab and \n as a newline: no error, just different data. With the two-line text above, the raw line break makes JSON.parse in Node 22 throw SyntaxError: Bad control character in string literal in JSON at position 16. Let the serializer do it.
Python
json.dumps also escapes non-ASCII characters by default, as \u escapes and surrogate pairs. ensure_ascii=False keeps them as they are, and both forms are valid JSON for the same text:
import json
text = 'She said "hi"\nC:\\temp'
print(json.dumps(text))
print(json.dumps('café 🦆'))
print(json.dumps('café 🦆', ensure_ascii=False))
"She said \"hi\"\nC:\\temp"
"caf\u00e9 \ud83e\udd86"
"café 🦆"
jq and Go
In the shell, jq turns a variable into a JSON string:
text='She said "hi"
C:\temp'
jq -n --arg s "$text" '$s'
# "She said \"hi\"\nC:\\temp"
jq -Rs . note.txt does the same for a whole file, and keeps the file’s last newline as a final \n.
Go’s json.Marshal escapes <, > and & as well, so its output is safe inside an HTML <script> element. An Encoder with SetEscapeHTML(false) leaves them alone:
out, _ := json.Marshal(`She said "hi" <b>&</b>`)
fmt.Println(string(out))
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
enc.SetEscapeHTML(false)
enc.Encode(`She said "hi" <b>&</b>`)
fmt.Print(buf.String())
"She said \"hi\" \u003cb\u003e\u0026\u003c/b\u003e"
"She said \"hi\" <b>&</b>"