88import json
99import logging
1010from pathlib import Path
11- from typing import TYPE_CHECKING , Dict , Type , TypeVar
11+ from typing import TYPE_CHECKING , Type , TypeVar
1212
1313from power_grid_model_ds ._core .model .arrays .base .array import FancyArray
1414
2424logger = logging .getLogger (__name__ )
2525
2626
27- def _restore_grid_values (grid , json_data : Dict ) -> None :
28- """Restore arrays to the grid."""
29- for attr_name , attr_values in json_data .items ():
30- if not hasattr (grid , attr_name ):
31- continue
32-
33- if not issubclass (getattr (grid , attr_name ).__class__ , FancyArray ):
34- expected_type = grid .__dataclass_fields__ [attr_name ].type
35- cast_value = expected_type (attr_values )
36- setattr (grid , attr_name , cast_value )
37- continue
38-
39- try :
40- array_field = grid .find_array_field (getattr (grid , attr_name ).__class__ )
41- matched_columns = {
42- col : attr_values ["data" ][col ] for col in array_field .type ().columns if col in attr_values ["data" ]
43- }
44- restored_array = array_field .type (** matched_columns )
45- setattr (grid , attr_name , restored_array )
46- except (AttributeError , KeyError , ValueError , TypeError ) as e :
47- # Handle restoration failures:
48- # - KeyError: missing "dtype" or "data" keys
49- # - ValueError/TypeError: invalid dtype string or data conversion
50- # - AttributeError: grid methods/attributes missing
51- logger .warning (f"Failed to restore '{ attr_name } ': { e } " )
52-
53-
54- def _save_grid_to_json (
55- grid ,
56- path : Path ,
57- ** kwargs ,
58- ) -> Path :
27+ def save_grid_to_json (grid , path : Path , strict : bool = True , ** kwargs ) -> Path :
5928 """Save a Grid object to JSON format using power-grid-model serialization with extensions support.
6029
6130 Args:
6231 grid: The Grid object to serialize
6332 path: The file path to save to
33+ strict: Whether to raise an error if the grid object is not serializable.
6434 **kwargs: Keyword arguments forwarded to json.dump (for example, indent, sort_keys,
6535 ensure_ascii, etc.).
6636 Returns:
@@ -69,24 +39,25 @@ def _save_grid_to_json(
6939 path .parent .mkdir (parents = True , exist_ok = True )
7040
7141 serialized_data = {}
42+
7243 for field in dataclasses .fields (grid ):
7344 if field .name in ["graphs" , "_id_counter" ]:
7445 continue
7546
7647 field_value = getattr (grid , field .name )
77- if isinstance (field_value , (int , float , str , bool )):
78- serialized_data [field .name ] = field_value
79- continue
80-
81- if not isinstance (field_value , FancyArray ):
82- raise NotImplementedError (f"Serialization for field of type '{ type (field_value )} ' is not implemented." )
8348
84- if field_value .size == 0 :
49+ if isinstance (field_value , FancyArray ):
50+ serialized_data [field .name ] = {
51+ "data" : {name : field_value [name ].tolist () for name in field_value .dtype .names },
52+ }
8553 continue
8654
87- serialized_data [field .name ] = {
88- "data" : {name : field_value [name ].tolist () for name in field_value .dtype .names },
89- }
55+ try :
56+ json .dumps (field_value )
57+ except TypeError as e :
58+ if strict :
59+ raise
60+ logger .warning (f"Failed to serialize '{ field .name } ': { e } " )
9061
9162 # Write to file
9263 with open (path , "w" , encoding = "utf-8" ) as f :
@@ -95,7 +66,7 @@ def _save_grid_to_json(
9566 return path
9667
9768
98- def _load_grid_from_json (path : Path , target_grid_class : Type [G ]) -> G :
69+ def load_grid_from_json (path : Path , target_grid_class : Type [G ]) -> G :
9970 """Load a Grid object from JSON format with cross-type loading support.
10071
10172 Args:
@@ -108,7 +79,30 @@ def _load_grid_from_json(path: Path, target_grid_class: Type[G]) -> G:
10879 with open (path , "r" , encoding = "utf-8" ) as f :
10980 input_data = json .load (f )
11081
111- target_grid = target_grid_class .empty ()
112- _restore_grid_values (target_grid , input_data )
82+ grid = target_grid_class .empty ()
83+ _restore_grid_values (grid , input_data )
84+ graph_class = grid .graphs .__class__
85+ grid .graphs = graph_class .from_arrays (grid )
86+ return grid
87+
88+
89+ def _restore_grid_values (grid : G , json_data : dict ) -> None :
90+ """Restore arrays to the grid."""
91+ for attr_name , attr_values in json_data .items ():
92+ if not hasattr (grid , attr_name ):
93+ logger .warning (f"Unexpected attribute '{ attr_name } '" )
94+ continue
95+
96+ grid_attr = getattr (grid , attr_name )
97+ attr_class = grid_attr .__class__
98+ if isinstance (grid_attr , FancyArray ):
99+ if extra := set (attr_values ["data" ]) - set (grid_attr .columns ):
100+ logger .warning (f"{ attr_name } has extra columns: { extra } " )
101+
102+ matched_columns = {col : attr_values ["data" ][col ] for col in grid_attr .columns if col in attr_values ["data" ]}
103+ restored_array = attr_class (** matched_columns )
104+ setattr (grid , attr_name , restored_array )
105+ continue
113106
114- return target_grid
107+ # load other values
108+ setattr (grid , attr_name , attr_class (attr_values ))
0 commit comments