1+ from typing import Any
12from deepdiff .serialization import json_dumps
23
34
@@ -13,22 +14,23 @@ def _truncate(s, max_len):
1314 return s [:max_len - 5 ] + "..." + s [- 2 :]
1415
1516class JSONNode :
16- def __init__ (self , data , key = None ):
17+ def __init__ (self , data : Any , key = None ):
1718 """
1819 Build a tree node for the JSON data.
1920 If this node is a child of a dict, key is its key name.
2021 """
2122 self .key = key
23+ self .children_list : list [JSONNode ] = []
24+ self .children_dict : list [tuple [Any , JSONNode ]] = []
2225 if isinstance (data , dict ):
2326 self .type = "dict"
24- self .children = []
2527 # Preserve insertion order: list of (key, child) pairs.
2628 for k , v in data .items ():
2729 child = JSONNode (v , key = k )
28- self .children .append ((k , child ))
30+ self .children_dict .append ((k , child ))
2931 elif isinstance (data , list ):
3032 self .type = "list"
31- self .children = [JSONNode (item ) for item in data ]
33+ self .children_list = [JSONNode (item ) for item in data ]
3234 else :
3335 self .type = "primitive"
3436 # For primitives, use json.dumps to get a compact representation.
@@ -37,24 +39,25 @@ def __init__(self, data, key=None):
3739 except Exception :
3840 self .value = str (data )
3941
40- def full_repr (self ):
42+ def full_repr (self ) -> str :
4143 """Return the full minimized JSON representation (without trimming) for this node."""
4244 if self .type == "primitive" :
4345 return self .value
4446 elif self .type == "dict" :
4547 parts = []
46- for k , child in self .children :
48+ for k , child in self .children_dict :
4749 parts .append (f'"{ k } ":{ child .full_repr ()} ' )
4850 return "{" + "," .join (parts ) + "}"
4951 elif self .type == "list" :
50- parts = [child .full_repr () for child in self .children ]
52+ parts = [child .full_repr () for child in self .children_list ]
5153 return "[" + "," .join (parts ) + "]"
54+ return self .value
5255
5356 def full_weight (self ):
5457 """Return the character count of the full representation."""
5558 return len (self .full_repr ())
5659
57- def summarize (self , budget ):
60+ def _summarize (self , budget ) -> str :
5861 """
5962 Return a summary string for this node that fits within budget characters.
6063 The algorithm may drop whole sub-branches (for dicts) or truncate long primitives.
@@ -69,16 +72,17 @@ def summarize(self, budget):
6972 return self ._summarize_dict (budget )
7073 elif self .type == "list" :
7174 return self ._summarize_list (budget )
75+ return self .value
7276
73- def _summarize_dict (self , budget ):
77+ def _summarize_dict (self , budget ) -> str :
7478 # If the dict is empty, return {}
75- if not self .children :
79+ if not self .children_dict :
7680 return "{}"
7781 # Build a list of pairs with fixed parts:
7882 # Each pair: key_repr is f'"{key}":'
7983 # Also store the full (untrimmed) child representation.
8084 pairs = []
81- for k , child in self .children :
85+ for k , child in self .children_dict :
8286 key_repr = f'"{ k } ":'
8387 child_full = child .full_repr ()
8488 pair_full = key_repr + child_full
@@ -103,7 +107,7 @@ def _summarize_dict(self, budget):
103107 # Heuristic: while the representation is too long, drop the pair whose child_full is longest.
104108 while kept :
105109 # Sort kept pairs in original insertion order.
106- kept_sorted = sorted (kept , key = lambda p : self .children .index ((p ["key" ], p ["child" ])))
110+ kept_sorted = sorted (kept , key = lambda p : self .children_dict .index ((p ["key" ], p ["child" ])))
107111 current_n = len (kept_sorted )
108112 fixed = sum (len (p ["key_repr" ]) for p in kept_sorted ) + (current_n - 1 ) + 2
109113 remaining_budget = budget - fixed
@@ -116,7 +120,7 @@ def _summarize_dict(self, budget):
116120 child_summaries = []
117121 for p in kept_sorted :
118122 ideal = int (remaining_budget * (len (p ["child_full" ]) / total_child_full )) if total_child_full > 0 else 0
119- summary_child = p ["child" ].summarize (ideal )
123+ summary_child = p ["child" ]._summarize (ideal )
120124 child_summaries .append (summary_child )
121125 candidate = "{" + "," .join ([p ["key_repr" ] + s for p , s in zip (kept_sorted , child_summaries )]) + "}"
122126 if len (candidate ) <= budget :
@@ -127,17 +131,17 @@ def _summarize_dict(self, budget):
127131 # If nothing remains, return a truncated empty object.
128132 return _truncate ("{}" , budget )
129133
130- def _summarize_list (self , budget ):
134+ def _summarize_list (self , budget ) -> str :
131135 # If the list is empty, return []
132- if not self .children :
136+ if not self .children_list :
133137 return "[]"
134138 full_repr = self .full_repr ()
135139 if len (full_repr ) <= budget :
136140 return full_repr
137141 # For lists, show only the first element and an omission indicator if more elements exist.
138- suffix = ",..." if len (self .children ) > 1 else ""
142+ suffix = ",..." if len (self .children_list ) > 1 else ""
139143 inner_budget = budget - 2 - len (suffix ) # subtract brackets and suffix
140- first_summary = self .children [0 ].summarize (inner_budget )
144+ first_summary = self .children_list [0 ]._summarize (inner_budget )
141145 candidate = "[" + first_summary + suffix + "]"
142146 if len (candidate ) <= budget :
143147 return candidate
@@ -150,4 +154,4 @@ def summarize(data, max_length=200):
150154 ensuring the final string is no longer than self.max_length.
151155 """
152156 root = JSONNode (data )
153- return root .summarize (max_length ).replace ("{," , "{" )
157+ return root ._summarize (max_length ).replace ("{," , "{" )
0 commit comments