2633. Convert Object to JSON String 🔒
Problem Description
This problem asks you to implement a function that converts JavaScript values into their JSON string representation without using the built-in JSON.stringify
method.
The function should handle the following data types:
- null: Should return the string
"null"
- string: Should be wrapped in double quotes, e.g.,
"hello"
becomes"\"hello\""
- number: Should be converted to its string representation, e.g.,
42
becomes"42"
- boolean: Should return
"true"
or"false"
- array: Should return elements wrapped in square brackets separated by commas, e.g.,
[1, 2, 3]
becomes"[1,2,3]"
- object: Should return key-value pairs wrapped in curly braces, with keys as strings and separated by commas, e.g.,
{a: 1, b: 2}
becomes"{"a":1,"b":2}"
Key requirements:
- The output string should not contain extra spaces
- For objects, the order of keys in the output should match the order returned by
Object.keys()
- The function should recursively handle nested structures (arrays within objects, objects within arrays, etc.)
The solution uses a recursive approach where:
- Base cases handle primitives (
null
, strings, numbers, booleans) - For arrays, it maps each element through the same function recursively and joins them with commas
- For objects, it uses
Object.entries()
to get key-value pairs, converts both keys and values to JSON strings recursively, and formats them as"key":value
pairs joined by commas
Intuition
The key insight is that JSON stringification is inherently a recursive process - we need to handle each data type according to its JSON representation rules, and for composite types (arrays and objects), we need to recursively process their contents.
We can think of this problem as a tree traversal where:
- Leaf nodes are primitive values (null, strings, numbers, booleans)
- Internal nodes are containers (arrays and objects) that hold other values
Starting from the simplest cases, we handle each JavaScript type:
-
Primitives first: These are our base cases that don't require recursion.
null
becomes"null"
, strings get wrapped in quotes, numbers and booleans just need.toString()
-
Arrays: Once we know how to stringify individual elements, an array is just those stringified elements joined with commas and wrapped in brackets. The pattern
[element1,element2,...]
naturally emerges. -
Objects: Similar to arrays, but we need to handle key-value pairs. Since JSON requires keys to be strings, we stringify the key, add a colon, then stringify the value. The pattern
{"key1":value1,"key2":value2,...}
follows naturally.
The recursive nature becomes clear when we realize that arrays can contain objects, objects can contain arrays, and both can be nested to any depth. By having our function call itself for each element or value, we automatically handle any level of nesting.
Using Object.entries()
for objects is a natural choice because it gives us key-value pairs in the correct order, and we can easily transform each pair into the required "key":value
format. Similarly, Array.map()
is perfect for arrays since we need to transform each element individually before joining them together.
Solution Approach
The implementation uses a recursive function that performs type checking and applies the appropriate conversion for each data type:
1. Handle null case:
if (object === null) {
return 'null';
}
Since null
is technically an object in JavaScript but has special JSON representation, we check it first.
2. Handle string values:
if (typeof object === 'string') {
return `"${object}"`;
}
Strings are wrapped in double quotes using template literals.
3. Handle numbers and booleans:
if (typeof object === 'number' || typeof object === 'boolean') {
return object.toString();
}
These primitives are converted directly to strings using .toString()
.
4. Handle arrays:
if (Array.isArray(object)) {
return `[${object.map(jsonStringify).join(',')}]`;
}
- Use
Array.isArray()
to identify arrays (important since arrays are objects in JavaScript) - Map each element through
jsonStringify
recursively - Join the stringified elements with commas
- Wrap the result in square brackets
5. Handle objects:
if (typeof object === 'object') {
return `{${Object.entries(object)
.map(([key, value]) => `${jsonStringify(key)}:${jsonStringify(value)}`)
.join(',')}}`;
}
- Use
Object.entries()
to get an array of[key, value]
pairs - For each pair, stringify both the key and value recursively
- Format as
"key":value
(note the key is stringified to ensure it's wrapped in quotes) - Join all pairs with commas
- Wrap the result in curly braces
6. Default case:
return '';
Returns an empty string for any unexpected types (though in practice, the above cases should cover all valid JSON types).
The recursion naturally handles nested structures - when processing an array containing objects, the function calls itself on each object, which in turn may call itself on nested values, creating a depth-first traversal of the data structure.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through converting the object {name: "Alice", scores: [95, 87], active: true}
to its JSON string representation.
Step 1: Start with the root object
- Input:
{name: "Alice", scores: [95, 87], active: true}
- Type check: It's an object (not null, not an array)
- Use
Object.entries()
to get:[["name", "Alice"], ["scores", [95, 87]], ["active", true]]
Step 2: Process first key-value pair ["name", "Alice"]
- Stringify key "name":
- Type: string → wrap in quotes →
"\"name\""
- Type: string → wrap in quotes →
- Stringify value "Alice":
- Type: string → wrap in quotes →
"\"Alice\""
- Type: string → wrap in quotes →
- Combine:
"\"name\":\"Alice\""
Step 3: Process second key-value pair ["scores", [95, 87]]
- Stringify key "scores":
- Type: string → wrap in quotes →
"\"scores\""
- Type: string → wrap in quotes →
- Stringify value
[95, 87]
:- Type: array → recursively process each element
- Element 95: Type number →
.toString()
→"95"
- Element 87: Type number →
.toString()
→"87"
- Join with comma and wrap in brackets →
"[95,87]"
- Combine:
"\"scores\":[95,87]"
Step 4: Process third key-value pair ["active", true]
- Stringify key "active":
- Type: string → wrap in quotes →
"\"active\""
- Type: string → wrap in quotes →
- Stringify value
true
:- Type: boolean →
.toString()
→"true"
- Type: boolean →
- Combine:
"\"active\":true"
Step 5: Combine all pairs
- Join the three formatted pairs with commas:
"\"name\":\"Alice\",\"scores\":[95,87],\"active\":true"
- Wrap in curly braces:
"{\"name\":\"Alice\",\"scores\":[95,87],\"active\":true}"
Final Result: "{\"name\":\"Alice\",\"scores\":[95,87],\"active\":true}"
The recursion depth in this example goes two levels deep: the root object contains an array, which contains numbers. Each level applies the appropriate formatting rules, and the recursive calls ensure that nested structures are properly converted regardless of their depth.
Solution Implementation
1def jsonStringify(object):
2 """
3 Converts a Python value to a JSON string representation
4
5 Args:
6 object: The value to be converted to a JSON string
7
8 Returns:
9 str: The JSON string representation of the input value
10 """
11 # Handle None value (equivalent to null in JavaScript)
12 if object is None:
13 return 'null'
14
15 # Handle string type - wrap in double quotes
16 if isinstance(object, str):
17 return f'"{object}"'
18
19 # Handle boolean type - convert to lowercase string ('true' or 'false')
20 if isinstance(object, bool):
21 return 'true' if object else 'false'
22
23 # Handle number types (int and float) - convert directly to string
24 if isinstance(object, (int, float)):
25 return str(object)
26
27 # Handle list type (array in JavaScript) - recursively stringify each element
28 if isinstance(object, list):
29 array_elements = [jsonStringify(element) for element in object]
30 return f"[{','.join(array_elements)}]"
31
32 # Handle dictionary type (object in JavaScript) - recursively stringify each key-value pair
33 if isinstance(object, dict):
34 key_value_pairs = []
35 for key, value in object.items():
36 # Convert key to string and stringify both key and value
37 stringified_pair = f'{jsonStringify(str(key))}:{jsonStringify(value)}'
38 key_value_pairs.append(stringified_pair)
39 return f"{{{','.join(key_value_pairs)}}}"
40
41 # Return empty string for unsupported types
42 return ''
43
1/**
2 * Converts a Java object to a JSON string representation
3 * @param object - The value to be converted to a JSON string
4 * @return The JSON string representation of the input value
5 */
6public class JsonStringifier {
7
8 public static String jsonStringify(Object object) {
9 // Handle null value
10 if (object == null) {
11 return "null";
12 }
13
14 // Handle string type - wrap in double quotes
15 if (object instanceof String) {
16 return "\"" + object + "\"";
17 }
18
19 // Handle number types - convert directly to string
20 if (object instanceof Number) {
21 return object.toString();
22 }
23
24 // Handle boolean type - convert directly to string
25 if (object instanceof Boolean) {
26 return object.toString();
27 }
28
29 // Handle array/list type - recursively stringify each element
30 if (object instanceof List) {
31 List<?> list = (List<?>) object;
32 StringBuilder arrayBuilder = new StringBuilder();
33 arrayBuilder.append("[");
34
35 // Process each element in the list
36 for (int i = 0; i < list.size(); i++) {
37 arrayBuilder.append(jsonStringify(list.get(i)));
38 // Add comma separator except for the last element
39 if (i < list.size() - 1) {
40 arrayBuilder.append(",");
41 }
42 }
43
44 arrayBuilder.append("]");
45 return arrayBuilder.toString();
46 }
47
48 // Handle primitive array types
49 if (object.getClass().isArray()) {
50 // Convert array to list for easier processing
51 List<Object> arrayList = new ArrayList<>();
52
53 // Check for different primitive array types
54 if (object instanceof int[]) {
55 for (int val : (int[]) object) {
56 arrayList.add(val);
57 }
58 } else if (object instanceof double[]) {
59 for (double val : (double[]) object) {
60 arrayList.add(val);
61 }
62 } else if (object instanceof boolean[]) {
63 for (boolean val : (boolean[]) object) {
64 arrayList.add(val);
65 }
66 } else if (object instanceof Object[]) {
67 arrayList = Arrays.asList((Object[]) object);
68 }
69
70 return jsonStringify(arrayList);
71 }
72
73 // Handle map/object type - recursively stringify each key-value pair
74 if (object instanceof Map) {
75 Map<?, ?> map = (Map<?, ?>) object;
76 StringBuilder objectBuilder = new StringBuilder();
77 objectBuilder.append("{");
78
79 int count = 0;
80 // Process each key-value pair in the map
81 for (Map.Entry<?, ?> entry : map.entrySet()) {
82 // Convert key to string and wrap in quotes
83 String key = jsonStringify(entry.getKey().toString());
84 // Recursively stringify the value
85 String value = jsonStringify(entry.getValue());
86
87 objectBuilder.append(key).append(":").append(value);
88
89 // Add comma separator except for the last entry
90 if (count < map.size() - 1) {
91 objectBuilder.append(",");
92 }
93 count++;
94 }
95
96 objectBuilder.append("}");
97 return objectBuilder.toString();
98 }
99
100 // Return empty string for unsupported types
101 return "";
102 }
103}
104
1#include <string>
2#include <vector>
3#include <unordered_map>
4#include <variant>
5#include <memory>
6#include <sstream>
7
8// Define a recursive variant type to handle JSON values
9struct JsonValue;
10using JsonNull = std::nullptr_t;
11using JsonBool = bool;
12using JsonNumber = double;
13using JsonString = std::string;
14using JsonArray = std::vector<std::shared_ptr<JsonValue>>;
15using JsonObject = std::unordered_map<std::string, std::shared_ptr<JsonValue>>;
16
17struct JsonValue {
18 std::variant<JsonNull, JsonBool, JsonNumber, JsonString, JsonArray, JsonObject> value;
19
20 // Constructors for different types
21 JsonValue() : value(nullptr) {}
22 JsonValue(std::nullptr_t) : value(nullptr) {}
23 JsonValue(bool b) : value(b) {}
24 JsonValue(double n) : value(n) {}
25 JsonValue(int n) : value(static_cast<double>(n)) {}
26 JsonValue(const std::string& s) : value(s) {}
27 JsonValue(const char* s) : value(std::string(s)) {}
28 JsonValue(const JsonArray& arr) : value(arr) {}
29 JsonValue(const JsonObject& obj) : value(obj) {}
30};
31
32/**
33 * Converts a JsonValue to a JSON string representation
34 * @param object - The JsonValue to be converted to a JSON string
35 * @return The JSON string representation of the input value
36 */
37std::string jsonStringify(const std::shared_ptr<JsonValue>& object) {
38 // Handle null pointer case
39 if (!object) {
40 return "null";
41 }
42
43 // Use visitor pattern to handle different types
44 return std::visit([](const auto& val) -> std::string {
45 using T = std::decay_t<decltype(val)>;
46
47 // Handle null value
48 if constexpr (std::is_same_v<T, JsonNull>) {
49 return "null";
50 }
51 // Handle boolean type - convert to "true" or "false"
52 else if constexpr (std::is_same_v<T, JsonBool>) {
53 return val ? "true" : "false";
54 }
55 // Handle number type - convert directly to string
56 else if constexpr (std::is_same_v<T, JsonNumber>) {
57 std::ostringstream oss;
58 oss << val;
59 return oss.str();
60 }
61 // Handle string type - wrap in double quotes
62 else if constexpr (std::is_same_v<T, JsonString>) {
63 return "\"" + val + "\"";
64 }
65 // Handle array type - recursively stringify each element
66 else if constexpr (std::is_same_v<T, JsonArray>) {
67 std::vector<std::string> array_elements;
68 for (const auto& element : val) {
69 array_elements.push_back(jsonStringify(element));
70 }
71
72 // Join array elements with comma
73 std::string result = "[";
74 for (size_t i = 0; i < array_elements.size(); ++i) {
75 if (i > 0) {
76 result += ",";
77 }
78 result += array_elements[i];
79 }
80 result += "]";
81 return result;
82 }
83 // Handle object type - recursively stringify each key-value pair
84 else if constexpr (std::is_same_v<T, JsonObject>) {
85 std::vector<std::string> key_value_pairs;
86 for (const auto& [key, value] : val) {
87 // Format as "key":value
88 std::string pair = "\"" + key + "\":" + jsonStringify(value);
89 key_value_pairs.push_back(pair);
90 }
91
92 // Join key-value pairs with comma
93 std::string result = "{";
94 for (size_t i = 0; i < key_value_pairs.size(); ++i) {
95 if (i > 0) {
96 result += ",";
97 }
98 result += key_value_pairs[i];
99 }
100 result += "}";
101 return result;
102 }
103
104 // Return empty string for unsupported types (should not reach here)
105 return "";
106 }, object->value);
107}
108
1/**
2 * Converts a JavaScript value to a JSON string representation
3 * @param object - The value to be converted to a JSON string
4 * @returns The JSON string representation of the input value
5 */
6function jsonStringify(object: any): string {
7 // Handle null value
8 if (object === null) {
9 return 'null';
10 }
11
12 // Handle string type - wrap in double quotes
13 if (typeof object === 'string') {
14 return `"${object}"`;
15 }
16
17 // Handle number and boolean types - convert directly to string
18 if (typeof object === 'number' || typeof object === 'boolean') {
19 return object.toString();
20 }
21
22 // Handle array type - recursively stringify each element
23 if (Array.isArray(object)) {
24 const arrayElements: string[] = object.map((element: any) => jsonStringify(element));
25 return `[${arrayElements.join(',')}]`;
26 }
27
28 // Handle object type - recursively stringify each key-value pair
29 if (typeof object === 'object') {
30 const objectEntries: [string, any][] = Object.entries(object);
31 const keyValuePairs: string[] = objectEntries.map(([key, value]: [string, any]) => {
32 return `${jsonStringify(key)}:${jsonStringify(value)}`;
33 });
34 return `{${keyValuePairs.join(',')}}`;
35 }
36
37 // Return empty string for undefined or other unsupported types
38 return '';
39}
40
Time and Space Complexity
Time Complexity: O(n)
where n
is the total number of primitive values (strings, numbers, booleans, null) in the entire object structure.
The analysis breaks down as follows:
- For primitive types (null, string, number, boolean):
O(1)
operations - For arrays:
O(m)
wherem
is the array length, as we recursively process each element - For objects:
O(k)
wherek
is the number of key-value pairs, as we recursively process each entry - Each primitive value in the nested structure is visited exactly once
- String concatenation and array joining operations are
O(n)
in total for all the characters/elements being joined
Space Complexity: O(d + n)
where d
is the maximum depth of the object/array nesting and n
is the total size of the output string.
The space complexity consists of:
- Call stack space:
O(d)
for the recursive calls, whered
is the maximum depth of nested objects/arrays - Output string space:
O(n)
wheren
is the length of the final JSON string representation - Intermediate strings and arrays created during
map()
andjoin()
operations:O(n)
in total - The dominant factor is typically
O(n)
for the output string size, but in deeply nested structures with small data,O(d)
could be significant
Common Pitfalls
1. Boolean Handling Order in Python
The most critical pitfall in the Python implementation is the order of type checking for booleans. In Python, bool
is a subclass of int
, so isinstance(True, int)
returns True
. This means if you check for integers before booleans, boolean values will be incorrectly converted to "1" or "0" instead of "true" or "false".
Incorrect Implementation:
# Wrong order - this will treat booleans as numbers
if isinstance(object, (int, float)):
return str(object)
if isinstance(object, bool):
return 'true' if object else 'false'
Correct Implementation:
# Check boolean BEFORE checking for numbers
if isinstance(object, bool):
return 'true' if object else 'false'
if isinstance(object, (int, float)):
return str(object)
2. String Escaping for Special Characters
The current implementation doesn't handle special characters in strings that need escaping in JSON, such as quotes, backslashes, newlines, tabs, etc.
Problem Example:
jsonStringify('Hello "World"') # Returns: "Hello "World"" (invalid JSON) jsonStringify('Line1\nLine2') # Returns: "Line1 Line2" (invalid JSON)
Solution:
def escape_string(s):
"""Escape special characters in strings for JSON"""
escape_dict = {
'"': '\\"',
'\\': '\\\\',
'\n': '\\n',
'\r': '\\r',
'\t': '\\t',
'\b': '\\b',
'\f': '\\f'
}
result = ''
for char in s:
if char in escape_dict:
result += escape_dict[char]
elif ord(char) < 0x20:
# Handle other control characters
result += f'\\u{ord(char):04x}'
else:
result += char
return result
# Then in jsonStringify:
if isinstance(object, str):
return f'"{escape_string(object)}"'
3. Circular Reference Handling
The recursive implementation doesn't detect circular references, which will cause infinite recursion and a stack overflow.
Problem Example:
obj = {'a': 1} obj['self'] = obj # Circular reference jsonStringify(obj) # RecursionError: maximum recursion depth exceeded
Solution:
def jsonStringify(object, visited=None):
if visited is None:
visited = set()
# Check for circular references in objects and lists
if isinstance(object, (dict, list)):
obj_id = id(object)
if obj_id in visited:
raise ValueError("Circular reference detected")
visited.add(obj_id)
# ... rest of the implementation ...
# Remember to pass visited to recursive calls
if isinstance(object, list):
array_elements = [jsonStringify(element, visited.copy()) for element in object]
return f"[{','.join(array_elements)}]"
if isinstance(object, dict):
key_value_pairs = []
visited_copy = visited.copy()
for key, value in object.items():
stringified_pair = f'{jsonStringify(str(key), visited_copy)}:{jsonStringify(value, visited_copy)}'
key_value_pairs.append(stringified_pair)
return f"{{{','.join(key_value_pairs)}}}"
4. Special Numeric Values
JSON doesn't support special numeric values like NaN
, Infinity
, or -Infinity
, but Python's float
type does. The current implementation would incorrectly convert these to their string representations.
Problem Example:
jsonStringify(float('nan')) # Returns: "nan" (invalid JSON)
jsonStringify(float('inf')) # Returns: "inf" (invalid JSON)
Solution:
import math
if isinstance(object, (int, float)):
if math.isnan(object) or math.isinf(object):
return 'null' # Standard JSON practice
return str(object)
Which data structure is used to implement recursion?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!