Skip to content

Commit c29fab0

Browse files
committed
fix order in serialisation of keyword indices
1 parent acebcf9 commit c29fab0

File tree

3 files changed

+21
-4
lines changed

3 files changed

+21
-4
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d"
1313
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
1414
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1515
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
16+
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
1617
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1718
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
1819

@@ -31,6 +32,7 @@ Distributions = "0.25"
3132
JSON = "0.19 - 0.21, 1"
3233
LinearAlgebra = "<0.0.1, 1"
3334
MacroTools = "0.5"
35+
OrderedCollections = "1.8.1"
3436
Random = "1.6"
3537
StatsBase = "0.32, 0.33, 0.34"
3638
julia = "1.10.8"

src/varname/serialize.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
### Serialisation to JSON / string
22

33
using JSON: JSON
4+
using OrderedCollections: OrderedDict
45

56
# String constants for each index type that we support serialisation /
67
# deserialisation of
@@ -70,6 +71,10 @@ then implement the following two methods:
7071
type (perhaps prefixed with module qualifiers) as this value to avoid
7172
clashes. The remainder of the dictionary can have any structure you like.
7273
74+
Note that `Base.Dict` does not guarantee key order, so if you need to preserve
75+
key order for fidelity in serialisation, consider returning an
76+
`OrderedCollections.OrderedDict` instead.
77+
7378
2. Suppose the value of `index_to_dict(i)["type"]` is `"MyModule.MyIndexType"`.
7479
You should then implement the corresponding method
7580
`AbstractPPL.dict_to_index(::Val{Symbol("MyModule.MyIndexType")}, dict)`,
@@ -119,8 +124,7 @@ function optic_to_dict(i::Index)
119124
# For some reason if you don't do the isempty check, it gets serialised as `{}`
120125
# rather than `[]`
121126
"ix" => isempty(i.ix) ? [] : collect(map(index_to_dict, i.ix)),
122-
# TODO(penelopeysm): This is potentially lossy since order is not guaranteed
123-
"kw" => Dict(String(x) => index_to_dict(y) for (x, y) in pairs(i.kw)),
127+
"kw" => OrderedDict(String(x) => index_to_dict(y) for (x, y) in pairs(i.kw)),
124128
"child" => optic_to_dict(i.child),
125129
)
126130
end
@@ -144,7 +148,7 @@ function varname_to_dict(vn::VarName)
144148
return Dict("sym" => getsym(vn), "optic" => optic_to_dict(getoptic(vn)))
145149
end
146150

147-
function dict_to_varname(dict::Dict{<:AbstractString,Any})
151+
function dict_to_varname(dict::AbstractDict{<:AbstractString,Any})
148152
return VarName{Symbol(dict["sym"])}(dict_to_optic(dict["optic"]))
149153
end
150154

@@ -178,4 +182,4 @@ Convert a string representation of a `VarName` back to a `VarName`. The string
178182
should have been generated by `varname_to_string`.
179183
"""
180184
string_to_varname(str::AbstractString) =
181-
dict_to_varname(JSON.parse(str; dicttype=Dict{String,Any}))
185+
dict_to_varname(JSON.parse(str; dicttype=OrderedDict{String,Any}))

test/varname/serialize.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ using Test
3838
@varname(z[2:5, :], false),
3939
@varname(z[2:5, :], true),
4040
@varname(x[i=1]),
41+
@varname(x[j=2, i=1]),
42+
@varname(x[i=1, j=2]),
4143
@varname(x[].a[j=2].b[3, 4, 5, [6]]),
4244
]
4345
for vn in vns
@@ -76,6 +78,15 @@ using Test
7678

7779
# Serialisation should now work
7880
@test string_to_varname(varname_to_string(vn)) == vn
81+
82+
# Delete the methods to avoid side effects when running tests again.
83+
Base.delete_method(which(AbstractPPL.index_to_dict, (InvertedIndex{Int},)))
84+
Base.delete_method(
85+
which(
86+
AbstractPPL.dict_to_index,
87+
(Val{Symbol("InvertedIndices.InvertedIndex")}, Dict),
88+
),
89+
)
7990
end
8091
end
8192

0 commit comments

Comments
 (0)