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
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user