Tighten JSON path helper performance

This commit is contained in:
2026-07-21 11:19:37 +00:00
parent 4ad9e93b63
commit 0fae1f5f20

View File

@@ -28,9 +28,9 @@ def _parse_json_path(path):
return tuple(segments) return tuple(segments)
def _get_json_path_value(data, path): def _get_json_path_value_segments(data, segments):
current = data current = data
for segment in _parse_json_path(path): for segment in segments:
if isinstance(segment, int): if isinstance(segment, int):
if not isinstance(current, list) or segment >= len(current): if not isinstance(current, list) or segment >= len(current):
return None return None
@@ -43,9 +43,13 @@ def _get_json_path_value(data, path):
return current return current
def _has_json_path(data, path): def _get_json_path_value(data, path):
return _get_json_path_value_segments(data, _parse_json_path(path))
def _has_json_path_segments(data, segments):
current = data current = data
for segment in _parse_json_path(path): for segment in segments:
if isinstance(segment, int): if isinstance(segment, int):
if not isinstance(current, list) or segment >= len(current): if not isinstance(current, list) or segment >= len(current):
return False return False
@@ -58,16 +62,20 @@ def _has_json_path(data, path):
return True return True
def _has_json_path(data, path):
return _has_json_path_segments(data, _parse_json_path(path))
def _build_missing_container(next_segment): def _build_missing_container(next_segment):
return [] if isinstance(next_segment, int) else {} return [] if isinstance(next_segment, int) else {}
def _set_json_path_value_recursive(current, segments, value): def _set_json_path_value_recursive(current, segments, position, value):
if not segments: if position >= len(segments):
return value return value
segment = segments[0] segment = segments[position]
is_last = len(segments) == 1 is_last = position == len(segments) - 1
if isinstance(segment, int): if isinstance(segment, int):
if not isinstance(current, list): if not isinstance(current, list):
@@ -83,8 +91,8 @@ def _set_json_path_value_recursive(current, segments, value):
next_current = result[segment] next_current = result[segment]
if not isinstance(next_current, (dict, list)): if not isinstance(next_current, (dict, list)):
next_current = _build_missing_container(segments[1]) next_current = _build_missing_container(segments[position + 1])
updated = _set_json_path_value_recursive(next_current, segments[1:], value) updated = _set_json_path_value_recursive(next_current, segments, position + 1, value)
if updated is None: if updated is None:
return None return None
result[segment] = updated result[segment] = updated
@@ -100,31 +108,34 @@ def _set_json_path_value_recursive(current, segments, value):
next_current = result.get(segment) next_current = result.get(segment)
if not isinstance(next_current, (dict, list)): if not isinstance(next_current, (dict, list)):
next_current = _build_missing_container(segments[1]) next_current = _build_missing_container(segments[position + 1])
updated = _set_json_path_value_recursive(next_current, segments[1:], value) updated = _set_json_path_value_recursive(next_current, segments, position + 1, value)
if updated is None: if updated is None:
return None return None
result[segment] = updated result[segment] = updated
return result return result
def _set_json_path_value(data, path, value): def _set_json_path_value_segments(data, segments, value):
if not isinstance(data, dict): if not isinstance(data, dict):
return None return None
segments = _parse_json_path(path)
if not segments: if not segments:
return value return value
return _set_json_path_value_recursive(data, segments, value) return _set_json_path_value_recursive(data, segments, 0, value)
def _remove_json_path_value_recursive(current, segments): def _set_json_path_value(data, path, value):
if not segments: return _set_json_path_value_segments(data, _parse_json_path(path), value)
def _remove_json_path_value_recursive(current, segments, position):
if position >= len(segments):
return _clone_container(current) return _clone_container(current)
segment = segments[0] segment = segments[position]
is_last = len(segments) == 1 is_last = position == len(segments) - 1
if isinstance(segment, int): if isinstance(segment, int):
if not isinstance(current, list) or segment >= len(current): if not isinstance(current, list) or segment >= len(current):
@@ -135,7 +146,7 @@ def _remove_json_path_value_recursive(current, segments):
del result[segment] del result[segment]
return result return result
updated = _remove_json_path_value_recursive(result[segment], segments[1:]) updated = _remove_json_path_value_recursive(result[segment], segments, position + 1)
if updated is None: if updated is None:
return None return None
result[segment] = updated result[segment] = updated
@@ -149,18 +160,21 @@ def _remove_json_path_value_recursive(current, segments):
del result[segment] del result[segment]
return result return result
updated = _remove_json_path_value_recursive(result[segment], segments[1:]) updated = _remove_json_path_value_recursive(result[segment], segments, position + 1)
if updated is None: if updated is None:
return None return None
result[segment] = updated result[segment] = updated
return result return result
def _remove_json_path_value(data, path): def _remove_json_path_value_segments(data, segments):
segments = _parse_json_path(path)
if not segments: if not segments:
return _clone_container(data) return _clone_container(data)
return _remove_json_path_value_recursive(data, segments) return _remove_json_path_value_recursive(data, segments, 0)
def _remove_json_path_value(data, path):
return _remove_json_path_value_segments(data, _parse_json_path(path))
def _merge_json_objects(base_object, overlay_object): def _merge_json_objects(base_object, overlay_object):
@@ -211,7 +225,7 @@ def _unflatten_json_object(flat_object):
for path, value in flat_object.items(): for path, value in flat_object.items():
if not isinstance(path, str): if not isinstance(path, str):
return None return None
result = _set_json_path_value(result, path, value) result = _set_json_path_value_segments(result, _parse_json_path(path), value)
if result is None: if result is None:
return None return None
return result return result
@@ -369,10 +383,13 @@ class DumasJSONPickFieldsNode:
result = {} result = {}
for raw_path in paths.splitlines(): for raw_path in paths.splitlines():
path = raw_path.strip() path = raw_path.strip()
if not path or not _has_json_path(json_object, path): if not path:
continue continue
value = _get_json_path_value(json_object, path) segments = _parse_json_path(path)
result = _set_json_path_value(result, path, value) if not _has_json_path_segments(json_object, segments):
continue
value = _get_json_path_value_segments(json_object, segments)
result = _set_json_path_value_segments(result, segments, value)
if result is None: if result is None:
return (None,) return (None,)
return (result,) return (result,)