Taro Logo

Convert Object to JSON String

Medium
Asked by:
Profile picture
15 views

Given an object, return a JSON string representing it. You can assume that the object only contains strings, integers, booleans, arrays, and other objects.

Example 1:

Input: obj = {"name":"Bob","age":13,"verified":true}
Output: "{\"name\":\"Bob\",\"age\":13,\"verified\":true}"
Explanation:
The object is serialized into a JSON string.

Example 2:

Input: obj = {"key":"value"}
Output: "{\"key\":\"value\"}"

Example 3:

Input: obj = { "innerObj": { "key": "value" }, "value": 42 }
Output: '{"innerObj":{"key":"value"},"value":42}'

Constraints:

  • obj is a valid JSON object.
  • 2 <= JSON.stringify(obj).length <= 105

Solution


Clarifying Questions

When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:

  1. What data types can the object's values be? Should I expect primitive types, other objects, arrays, null values, or a combination?
  2. Are there any limitations on the depth or complexity of the object? Could it contain circular references?
  3. How should I handle special values like Infinity, NaN, or undefined in the object?
  4. Is there a specific format or encoding required for the output JSON string (e.g., dealing with special characters like quotes or backslashes)?
  5. Should the order of the keys in the resulting JSON string be preserved from the input object, or is any order acceptable?

Brute Force Solution

Approach

The brute force approach to converting an object to a JSON string involves handling each possible data type separately and recursively. It essentially inspects every field in the object and transforms it based on its type. This method explores all potential string representations until the entire object is converted.

Here's how the algorithm would work step-by-step:

  1. First, check if the input is a simple type, like a number, text, true, false, or null. If so, convert it directly to its text representation.
  2. If the input is a list or collection, go through each item in the list one by one. Convert each item individually using these same steps, and then combine the converted items into a text string representing the entire list.
  3. If the input is a dictionary or object with key-value pairs, look at each key-value pair individually. Convert both the key and the value into their text representation, and combine them into a text string representing the key-value pair.
  4. Make sure to add special characters like commas, colons, brackets, and braces in the correct places to make the final text follow the correct JSON structure.
  5. Repeat these steps for every nested object, list, or dictionary within the original object, until everything is converted to a single, complete text string.
  6. Handle any special characters or edge cases, such as empty lists or objects, to avoid errors in the final JSON output.

Code Implementation

def convert_to_json_string(object_to_convert):

    if object_to_convert is None:
        return "null"

    if isinstance(object_to_convert, bool):
        return "true" if object_to_convert else "false"

    if isinstance(object_to_convert, (int, float)): # Numbers are converted directly
        return str(object_to_convert)

    if isinstance(object_to_convert, str):
        return '"' + object_to_convert + '"'

    if isinstance(object_to_convert, list):
        list_elements = []
        for element in object_to_convert:
            list_elements.append(convert_to_json_string(element))

        return '[' + ','.join(list_elements) + ']'

    if isinstance(object_to_convert, dict):
        # Dictionaries need special formatting of keys and values
        dictionary_elements = []
        for key, value in object_to_convert.items():

            key_string = convert_to_json_string(key)
            value_string = convert_to_json_string(value)

            dictionary_elements.append(key_string + ':' + value_string)

        # Construct the JSON representation of the dictionary
        return '{' + ','.join(dictionary_elements) + '}'

    # Raise exception in case we cannot serialize the given object
    raise TypeError(f"Object of type '{type(object_to_convert).__name__}' is not JSON serializable")

Big(O) Analysis

Time Complexity
O(n)The time complexity is determined by the structure of the input object. The algorithm visits each element in the object (or nested objects, lists, dictionaries) exactly once to convert it to its JSON string representation. Thus, if n is the total number of elements (primitive values, keys, objects, list items) within the object, the algorithm performs a constant amount of work for each of these elements. The overall complexity is therefore directly proportional to the number of elements in the input, resulting in O(n).
Space Complexity
O(D)The space complexity of this recursive approach is primarily determined by the maximum depth of the recursion, D, where D represents the maximum nesting level of objects, lists, and dictionaries within the input. Each recursive call adds a new frame to the call stack, storing local variables and function arguments for that level of nesting. In the worst-case scenario, the object is deeply nested, leading to a call stack of depth D. Therefore, the auxiliary space used is proportional to the maximum depth of nesting.

Optimal Solution

Approach

To transform an object into a text format resembling its structure, we need to handle different types of data within the object. The best approach is to systematically examine each part of the object and convert it into the correct text representation while following specific formatting rules.

Here's how the algorithm would work step-by-step:

  1. First, check what kind of thing we are dealing with. It could be a simple value like a number or text, or it could be a more complicated structure like another object or a list.
  2. If it's a simple value, just convert it directly into text format based on its type. For text, make sure to add quotation marks.
  3. If it's another object, we'll need to go through each piece of information inside that object and convert each piece into text format. Then, surround all these pieces with curly braces.
  4. If it's a list, we'll convert each item in the list into text format, just like we handled single values or other objects. Then, surround all these converted items with square brackets.
  5. For each object, also make sure to add commas to separate each pair of information within it.
  6. Remember to handle special cases, like when a value is 'nothing' or 'missing'. Convert these to the text 'null'.
  7. Finally, piece everything together according to the structural hierarchy of the original object. Start with the outside layer, work your way inward, and remember to add all the necessary text markers (like curly braces, square brackets, commas, and quotation marks).

Code Implementation

def convert_object_to_json_string(object_to_convert):

    if object_to_convert is None:
        return "null"

    object_type = type(object_to_convert)

    if object_type is str:
        return '"' + object_to_convert + '"'
    elif object_type is int or object_type is float or object_type is bool:
        return str(object_to_convert)
    elif object_type is list:
        # Recursively convert each list element to JSON.
        json_list = [convert_object_to_json_string(item) for item in object_to_convert]
        return '[' + ','.join(json_list) + ']'
    elif object_type is dict:
        # This block handles dictionaries/objects.
        json_pairs = []
        for key, value in object_to_convert.items():
            json_key = convert_object_to_json_string(key)
            json_value = convert_object_to_json_string(value)
            json_pairs.append(json_key + ':' + json_value)

        # Join key-value pairs with commas, enclose in curly braces.
        return '{' + ','.join(json_pairs) + '}'
    else:
        return '"' + str(object_to_convert) + '"'

Big(O) Analysis

Time Complexity
O(n)The algorithm iterates through the object's structure, potentially traversing all its elements. In the worst-case scenario, where the object is deeply nested or contains a large number of key-value pairs or list elements, the algorithm needs to visit each of these elements once to convert them into their JSON string representation. Therefore, the time complexity is directly proportional to the number of elements (n) in the object structure, resulting in a time complexity of O(n).
Space Complexity
O(N)The algorithm's space complexity is primarily determined by the depth of nested objects and lists within the input object. In the worst-case scenario, the object could have a nesting depth of N, where N represents the total number of elements across all levels of nesting. Each level of nesting potentially adds a frame to the call stack due to recursion. Thus, the auxiliary space used by the recursion stack can grow linearly with the depth of nesting, leading to a space complexity of O(N).

Edge Cases

Null or undefined object input
How to Handle:
Return 'null' string to represent null/undefined objects in JSON.
Empty object input ({})
How to Handle:
Return '{}' to represent an empty JSON object.
Object with circular references
How to Handle:
Detect cycles using a Set to track visited objects and throw an error or return 'null' to prevent infinite recursion.
Object containing primitive types (string, number, boolean, null)
How to Handle:
Convert primitive values directly to their JSON representation (e.g., number to string, true to 'true').
Object containing arrays of different data types
How to Handle:
Recursively convert array elements to their JSON representations, handling nested objects and arrays.
Object with properties containing special characters (e.g., quotes, backslashes, newline)
How to Handle:
Properly escape special characters in property names and string values using backslashes.
Object with Date objects
How to Handle:
Convert Date objects to their ISO string representation using toISOString().
Large object with many nested levels
How to Handle:
Implement iterative solution to avoid stack overflow errors or limit the recursion depth and return an error.