Optimize Dumas JSON node hot paths
This commit is contained in:
@@ -1,10 +1,20 @@
|
||||
import copy
|
||||
from functools import lru_cache
|
||||
from itertools import islice
|
||||
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):
|
||||
if not path.strip():
|
||||
return []
|
||||
return ()
|
||||
|
||||
segments = []
|
||||
for raw_segment in path.split("."):
|
||||
@@ -15,7 +25,7 @@ def _parse_json_path(path):
|
||||
segments.append(int(segment))
|
||||
else:
|
||||
segments.append(segment)
|
||||
return segments
|
||||
return tuple(segments)
|
||||
|
||||
|
||||
def _get_json_path_value(data, path):
|
||||
@@ -33,6 +43,56 @@ def _get_json_path_value(data, path):
|
||||
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):
|
||||
if not isinstance(data, dict):
|
||||
return None
|
||||
@@ -41,48 +101,20 @@ def _set_json_path_value(data, path, value):
|
||||
if not segments:
|
||||
return value
|
||||
|
||||
result = copy.deepcopy(data)
|
||||
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
|
||||
return _set_json_path_value_recursive(data, segments, value)
|
||||
|
||||
|
||||
def _merge_json_objects(base_object, overlay_object):
|
||||
if not isinstance(base_object, dict) or not isinstance(overlay_object, dict):
|
||||
return None
|
||||
|
||||
merged = copy.deepcopy(base_object)
|
||||
merged = dict(base_object)
|
||||
for key, value in overlay_object.items():
|
||||
if isinstance(merged.get(key), dict) and isinstance(value, dict):
|
||||
merged[key] = _merge_json_objects(merged[key], value)
|
||||
base_value = merged.get(key)
|
||||
if isinstance(base_value, dict) and isinstance(value, dict):
|
||||
merged[key] = _merge_json_objects(base_value, value)
|
||||
else:
|
||||
merged[key] = copy.deepcopy(value)
|
||||
merged[key] = _clone_container(value)
|
||||
return merged
|
||||
|
||||
|
||||
@@ -290,13 +322,12 @@ class DumasJSONObjectIteratorNode:
|
||||
if not isinstance(json_input, dict):
|
||||
return (None, None, -1, 0)
|
||||
|
||||
items = list(json_input.items())
|
||||
total_items = len(items)
|
||||
total_items = len(json_input)
|
||||
if total_items == 0:
|
||||
return (None, None, -1, 0)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user