@@ -22,63 +22,98 @@ _apply_over_grid(::Nothing, grid) = nothing
2222"""
2323Convert Dict{Symbol,Any} to Dict{String,Any} for JSON serialization.
2424Only serializes JSON-compatible types (numbers, strings, bools, arrays, dicts).
25+ Returns a tuple: (serialized_dict, symbol_keys) where symbol_keys tracks which values were Symbols.
2526"""
26- function _serialize_infos (infos:: Dict{Symbol,Any} ):: Dict{String,Any}
27+ function _serialize_infos (infos:: Dict{Symbol,Any} ):: Tuple{ Dict{String,Any},Vector{String} }
2728 result = Dict {String,Any} ()
29+ symbol_keys = String[]
2830 for (k, v) in infos
29- result[string (k)] = _serialize_value (v)
31+ key_str = string (k)
32+ serialized_value, nested_symbols = _serialize_value (v, key_str)
33+ result[key_str] = serialized_value
34+ append! (symbol_keys, nested_symbols)
3035 end
31- return result
36+ return ( result, symbol_keys)
3237end
3338
3439"""
3540Serialize a single value to JSON-compatible format.
41+ Returns a tuple: (serialized_value, symbol_paths) where symbol_paths tracks Symbol locations.
3642"""
37- function _serialize_value (v)
43+ function _serialize_value (v, path :: String = " " )
3844 if v isa Number || v isa String || v isa Bool || isnothing (v)
39- return v
45+ return (v, String[])
4046 elseif v isa Symbol
41- return string (v)
47+ # Mark this path as containing a Symbol
48+ return (string (v), [path])
4249 elseif v isa AbstractVector
43- return [_serialize_value (x) for x in v]
50+ serialized = []
51+ all_symbols = String[]
52+ for (i, x) in enumerate (v)
53+ val, syms = _serialize_value (x, " $(path) [$(i- 1 ) ]" )
54+ push! (serialized, val)
55+ append! (all_symbols, syms)
56+ end
57+ return (serialized, all_symbols)
4458 elseif v isa AbstractDict
4559 result = Dict {String,Any} ()
60+ all_symbols = String[]
4661 for (dk, dv) in v
47- result[string (dk)] = _serialize_value (dv)
62+ key_str = string (dk)
63+ new_path = isempty (path) ? key_str : " $(path) .$(key_str) "
64+ val, syms = _serialize_value (dv, new_path)
65+ result[key_str] = val
66+ append! (all_symbols, syms)
4867 end
49- return result
68+ return ( result, all_symbols)
5069 else
5170 # For non-serializable types, convert to string representation
52- return string (v)
71+ return ( string (v), String[] )
5372 end
5473end
5574
5675"""
5776Convert Dict{String,Any} back to Dict{Symbol,Any} after JSON deserialization.
77+ Uses symbol_keys metadata to restore Symbol types where they were originally present.
5878"""
59- function _deserialize_infos (blob):: Dict{Symbol,Any}
79+ function _deserialize_infos (
80+ blob, symbol_keys:: Vector{String} = String[]
81+ ):: Dict{Symbol,Any}
6082 if isnothing (blob) || isempty (blob)
6183 return Dict {Symbol,Any} ()
6284 end
6385 result = Dict {Symbol,Any} ()
6486 for (k, v) in blob
65- result[Symbol (k)] = _deserialize_value (v)
87+ result[Symbol (k)] = _deserialize_value (v, String (k), symbol_keys )
6688 end
6789 return result
6890end
6991
7092"""
7193Deserialize a single value from JSON format.
94+ Uses symbol_keys to restore Symbol types at the correct paths.
7295"""
73- function _deserialize_value (v)
74- if v isa Number || v isa String || v isa Bool || isnothing (v)
96+ function _deserialize_value (v, path :: String , symbol_keys :: Vector{String} )
97+ if v isa Number || v isa Bool || isnothing (v)
7598 return v
99+ elseif v isa String
100+ # Check if this path should be a Symbol
101+ if path in symbol_keys
102+ return Symbol (v)
103+ else
104+ return v
105+ end
76106 elseif v isa AbstractVector
77- return [_deserialize_value (x) for x in v]
107+ return [
108+ _deserialize_value (x, " $(path) [$(i- 1 ) ]" , symbol_keys) for
109+ (i, x) in enumerate (v)
110+ ]
78111 elseif v isa AbstractDict
79112 result = Dict {Symbol,Any} ()
80113 for (dk, dv) in v
81- result[Symbol (dk)] = _deserialize_value (dv)
114+ key_str = string (dk)
115+ new_path = isempty (path) ? key_str : " $(path) .$(key_str) "
116+ result[Symbol (dk)] = _deserialize_value (dv, new_path, symbol_keys)
82117 end
83118 return result
84119 else
@@ -145,10 +180,13 @@ function CTModels.export_ocp_solution(
145180 " boundary_constraints_dual" => CTModels. boundary_constraints_dual (sol), # ctVector or Nothing
146181 " variable_constraints_lb_dual" => CTModels. variable_constraints_lb_dual (sol), # ctVector or Nothing
147182 " variable_constraints_ub_dual" => CTModels. variable_constraints_ub_dual (sol), # ctVector or Nothing
148- # Additional solver infos (Dict{Symbol,Any} → Dict{String,Any} for JSON)
149- " infos" => _serialize_infos (CTModels. infos (sol)),
150183 )
151184
185+ # Serialize infos and get Symbol type metadata
186+ infos_serialized, symbol_keys = _serialize_infos (CTModels. infos (sol))
187+ blob[" infos" ] = infos_serialized
188+ blob[" infos_symbol_keys" ] = symbol_keys
189+
152190 open (filename * " .json" , " w" ) do io
153191 JSON3. pretty (io, blob)
154192 end
@@ -293,9 +331,11 @@ function CTModels.import_ocp_solution(
293331 variable_constraints_ub_dual = Vector {Float64} (blob[" variable_constraints_ub_dual" ])
294332 end
295333
296- # get additional solver infos
334+ # get additional solver infos with Symbol type restoration
335+ symbol_keys_raw = get (blob, " infos_symbol_keys" , String[])
336+ symbol_keys = collect (String, symbol_keys_raw) # Convert JSON3.Array/empty array to Vector{String}
297337 infos = if haskey (blob, " infos" )
298- _deserialize_infos (blob[" infos" ])
338+ _deserialize_infos (blob[" infos" ], symbol_keys )
299339 else
300340 Dict {Symbol,Any} ()
301341 end
0 commit comments