Optimize Dumas JSON node hot paths

This commit is contained in:
2026-07-21 10:57:07 +00:00
parent 5446a5c068
commit 24d8ac5e14

View File

@@ -1,10 +1,20 @@
import copy from functools import lru_cache
from itertools import islice
import json import json
def _clone_container(value):
if isinstance(value, dict):
return dict(value)
if isinstance(value, list):
return list(value)
return value
@lru_cache(maxsize=256)
def _parse_json_path(path): def _parse_json_path(path):
if not path.strip(): if not path.strip():
return [] return ()
segments = [] segments = []
for raw_segment in path.split("."): for raw_segment in path.split("."):
@@ -15,7 +25,7 @@ def _parse_json_path(path):
segments.append(int(segment)) segments.append(int(segment))
else: else:
segments.append(segment) segments.append(segment)
return segments return tuple(segments)
def _get_json_path_value(data, path): def _get_json_path_value(data, path):
@@ -33,6 +43,56 @@ def _get_json_path_value(data, path):
return current return current
def _build_missing_container(next_segment):
return [] if isinstance(next_segment, int) else {}
def _set_json_path_value_recursive(current, segments, value):
if not segments:
return value
segment = segments[0]
is_last = len(segments) == 1
if isinstance(segment, int):
if not isinstance(current, list):
return None
result = list(current)
while segment >= len(result):
result.append(None)
if is_last:
result[segment] = value
return result
next_current = result[segment]
if not isinstance(next_current, (dict, list)):
next_current = _build_missing_container(segments[1])
updated = _set_json_path_value_recursive(next_current, segments[1:], value)
if updated is None:
return None
result[segment] = updated
return result
if not isinstance(current, dict):
return None
result = dict(current)
if is_last:
result[segment] = value
return result
next_current = result.get(segment)
if not isinstance(next_current, (dict, list)):
next_current = _build_missing_container(segments[1])
updated = _set_json_path_value_recursive(next_current, segments[1:], value)
if updated is None:
return None
result[segment] = updated
return result
def _set_json_path_value(data, path, value): def _set_json_path_value(data, path, value):
if not isinstance(data, dict): if not isinstance(data, dict):
return None return None
@@ -41,48 +101,20 @@ def _set_json_path_value(data, path, value):
if not segments: if not segments:
return value return value
result = copy.deepcopy(data) return _set_json_path_value_recursive(data, segments, value)
current = result
for index, segment in enumerate(segments[:-1]):
next_segment = segments[index + 1]
if isinstance(segment, int):
if not isinstance(current, list):
return None
while segment >= len(current):
current.append({} if not isinstance(next_segment, int) else [])
current = current[segment]
continue
if segment not in current or not isinstance(current[segment], (dict, list)):
current[segment] = [] if isinstance(next_segment, int) else {}
current = current[segment]
last_segment = segments[-1]
if isinstance(last_segment, int):
if not isinstance(current, list):
return None
while last_segment >= len(current):
current.append(None)
current[last_segment] = value
return result
if not isinstance(current, dict):
return None
current[last_segment] = value
return result
def _merge_json_objects(base_object, overlay_object): def _merge_json_objects(base_object, overlay_object):
if not isinstance(base_object, dict) or not isinstance(overlay_object, dict): if not isinstance(base_object, dict) or not isinstance(overlay_object, dict):
return None return None
merged = copy.deepcopy(base_object) merged = dict(base_object)
for key, value in overlay_object.items(): for key, value in overlay_object.items():
if isinstance(merged.get(key), dict) and isinstance(value, dict): base_value = merged.get(key)
merged[key] = _merge_json_objects(merged[key], value) if isinstance(base_value, dict) and isinstance(value, dict):
merged[key] = _merge_json_objects(base_value, value)
else: else:
merged[key] = copy.deepcopy(value) merged[key] = _clone_container(value)
return merged return merged
@@ -290,13 +322,12 @@ class DumasJSONObjectIteratorNode:
if not isinstance(json_input, dict): if not isinstance(json_input, dict):
return (None, None, -1, 0) return (None, None, -1, 0)
items = list(json_input.items()) total_items = len(json_input)
total_items = len(items)
if total_items == 0: if total_items == 0:
return (None, None, -1, 0) return (None, None, -1, 0)
current_index = _resolve_iteration_index(index, mode, total_items) current_index = _resolve_iteration_index(index, mode, total_items)
key, value = items[current_index] key, value = next(islice(json_input.items(), current_index, None))
return (key, value, current_index, total_items) return (key, value, current_index, total_items)