How jsoncsv Handles Type Detection for Restoring Arrays: A Deep Dive into the Source Code
When converting flat key-value pairs back to nested JSON, jsoncsv detects arrays by checking if sibling keys form a complete sequence of 0-based integer indices.
The jsoncsv library provides bidirectional transformation between nested JSON objects and flattened CSV-compatible dictionaries. When restoring arrays from the flattened representation, the library must distinguish between JSON objects (Python dictionaries) and JSON arrays (Python lists) without explicit type metadata. This reconstruction logic is implemented in the core transformation module and relies on a strict contiguity check of index keys.
The Restoration Pipeline
The restoration process begins when you call restore() on a flattened dictionary. The library first converts the flat keys into hierarchical path components, then recursively rebuilds the original structure.
From Flat Keys to Leaf Nodes
In jsoncsv/jsontool.py, the restore() function initiates the process by splitting each key into a path sequence. Using either decode_safe_key (for safe mode) or standard string splitting, it generates a list of (path, value) tuples called leafs that represent the terminal nodes of the JSON tree structure.
Recursive Reconstruction with from_leaf
The from_leaf helper function groups these leaf nodes by their first path component and recursively processes the remaining path segments. At each level of recursion, it collects child_keys—the set of immediate sibling keys under the current path prefix. This set becomes the critical input for type detection.
Array Detection Logic in jsoncsv
The decision between creating a list or a dict hinges entirely on whether the sibling keys represent a valid array index sequence.
The is_array_index Helper Function
Located in jsoncsv/jsontool.py, the is_array_index function implements the core type detection algorithm. It receives a set of child keys and performs a strict validation:
- It builds a
key_mapset from the supplied sibling keys - It generates the expected integer range from
0tolen(keys)-1 - It returns
Trueonly if every integer in that expected range exists in the key map
If enable_str=True is configured, the function also accepts string equivalents of integers (e.g., "0", "1") as valid indices, allowing flexibility for string-keyed flat representations.
Strict Contiguity Requirements
When is_array_index(child_keys) evaluates to True, the from_leaf function sorts the children by their integer index and returns a Python list containing the child values in order. If the check fails—meaning the keys are non-numeric, non-contiguous, or missing indices—the function returns a standard Python dict instead.
This means that a group of keys like {"0", "1", "2"} becomes an array, while {"0", "2"} (missing index 1) becomes a dictionary with string keys "0" and "2".
Practical Examples of Array Restoration
The following examples demonstrate how jsoncsv reconstructs arrays from flattened data and handles edge cases where array detection fails.
Successful Array Reconstruction
When indices form a complete 0-based sequence, restore() correctly rebuilds the JSON array:
from jsoncsv.jsontool import expand, restore
# Original JSON containing an array
data = {"users": [{"name": "Alice"}, {"name": "Bob"}]}
# Flatten the structure
flat = expand(data) # {"users.0.name": "Alice", "users.1.name": "Bob"}
# Restore the original structure
restored = restore(flat) # {"users": [{"name": "Alice"}, {"name": "Bob"}]}
print(restored == data) # True
Fallback to Object for Irregular Keys
If the flattened keys contain gaps or non-sequential indices, the restoration process treats them as object keys rather than array indices:
from jsoncsv.jsontool import restore
# Missing index 1 breaks the sequence
flat = {"users.0.name": "Alice", "users.2.name": "Charlie"}
restored = restore(flat)
print(restored)
# Output: {'users': {'0': {'name': 'Alice'}, '2': {'name': 'Charlie'}}}
In this case, is_array_index returns False because index 1 is missing from the key set, causing from_leaf to return a dictionary with string keys "0" and "2" rather than a three-element list.
Summary
- Path splitting: The
restore()function injsoncsv/jsontool.pysplits flat keys into hierarchical paths usingdecode_safe_keyor string splitting. - Contiguity check: The
is_array_indexhelper validates that sibling keys form a complete sequence from0toN-1, optionally accepting string representations whenenable_str=True. - Type inference: Based on the
is_array_indexresult,from_leafreturns either a sortedlist(for valid arrays) or adict(for objects). - Data integrity: This detection mechanism ensures that only strictly sequential numeric keys reconstruct as arrays, preventing data corruption when original JSON objects happen to have numeric-looking keys.
Frequently Asked Questions
How does jsoncsv distinguish between a JSON object with numeric keys and a JSON array?
According to the jsoncsv/jsontool.py source code, the library applies a strict contiguity test via is_array_index. If the sibling keys under a path form a complete sequence starting at 0 with no gaps (e.g., 0, 1, 2), the structure becomes a list. If any index is missing or non-numeric, the structure becomes a dictionary, even if the keys look like numbers.
What happens if my flattened data has string indices like "0", "1" instead of integers?
The is_array_index function supports string indices when the enable_str parameter is set to True. It checks for the presence of both integer values and their string equivalents in the key_map. If all expected indices are present as strings (and enable_str=True), the restoration process treats them as array indices and returns a list.
Why did my array become a dictionary after restoring with jsoncsv?
This occurs when the flattened keys fail the contiguity check in is_array_index. Common causes include missing indices (e.g., having keys 0 and 2 but not 1) or using non-sequential numbering. The from_leaf function defaults to creating a dictionary whenever the keys do not represent a perfect 0-based sequence, ensuring data safety over aggressive array assumptions.
Can I force jsoncsv to treat specific keys as arrays during restoration?
The current implementation in jsoncsv does not provide a forced-type parameter in the public restore() API. Type detection is strictly automatic based on the is_array_index logic. To ensure correct array restoration, you must guarantee that your flattened data contains complete, sequential indices (0 through N-1) for all array elements before calling restore().
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →