Repository navigation
Description
The Kerchunk references spec has a weird property, and we might want to write out kerchunk references from VirtualiZarr differently to avoid it.
The Kerchunk v0 spec describes how keys and values (for example zarr (v2) keys) are to be laid out within a dict. The Kerchunk v1 spec wraps this up into a larger spec with another layer of dict. The problem is in the v0 part.
The Kerchunk v0 spec allows for two different ways to format any dict values that themselves have multiple key-value pairs:
- as more
dictobjects, so the whole thing is truly JSON-serializable, like this:{ ".zgroup": {"zarr_format": 2}, ".zattrs": {"Conventions": "UGRID-0.9.0\n"}, "x/.zattrs": {"_ARRAY_DIMENSIONS": ["node"]}, "x/.zarray": {"chunks": [9228245], "compressor": null, "dtype": "<f8"}, "x/0": ["s3://bucket/path/file.nc", 294094376, 73825960] } - that same information with the first layer of values serialized to strings, like this:
{ ".zgroup": "{\n \"zarr_format\": 2\n}", ".zattrs": "{\n \"Conventions\": \"UGRID-0.9.0\n\"}", "x/.zattrs": "{\n \"_ARRAY_DIMENSIONS\": [\n \"node\"\n ...", "x/.zarray": "{\n \"chunks\": [\n 9228245\n ],\n \"compressor\": null,\n \"dtype\": \"<f8\",\n ...", "x/0": ["s3://bucket/path/file.nc", 294094376, 73825960] }
Currently VirtualiZarr writes out the latter form, and some of our tests expect that. I think we (I) arbitrarily chose this form, not even fully understanding the Kerchunk spec at the time.
Now though, I can see that these is a problem with this partially-string-serialized form: the order of the keys in the string-serialized parts matter.
As @d-v-b points out in zarr-developers/zarr-python#3254, order is not supposed to matter in JSON. But two programs aiming to produce this spec format by first creating intermediate JSON objects and serializing them (i.e. treating them as true JSON) aren't guaranteed to end up with exactly compatible serialized string results, because the order of the keys in the string parts can end up different. This has now happened in VirtualiZarr (part of #677) due to upstream changes in Zarr's metadata classes, which reasonably assume order doesn't matter (see zarr-developers/zarr-python#3254).
This means the change in zarr-python, which by rights shouldn't matter, has technically caused a breaking change in the format that VirtualiZarr's .to_kerchunk writes out, which is only breaking because of an ambiguity in the Kerchunk references spec.
We have four choices I can see:
- Try to get zarr-python to preserve order of metadata keys (I don't think we should do that because JSON doesn't require that).
- Keep writing the same way in VirtualiZarr, adjust our tests slightly, and hence ignore the fact our output has changed. This may be okay - if everyone else using these references (i.e. likely just fsspec/kerchunk) is first deserializing them to
dictspiecemeal, then the problem washes out. - Manually alter the order of keys within the serialized string part before returning. This would be a weird hacky thing to do but would avoid altering the output and avoid changing tests. It would effectively be treating key order as an important part of the Kerchunk v0 spec to be respected.
- Change the format that VirtualiZarr's
.to_kerchunkwrites out to use the true JSON version of the Kerchunk v0 spec (i.e. fully-nested dicts). This is the "correct" solution from a data model perspective, but is also breaking, and I don't know if changing it will surface any other bugs.
I'm inclined to do (3) for now and keep this issue open in case we need to do (4) later (or in case kerchunk/fsspec needs to make any changes to handle the ambiguity).
cc @martindurant in case you have thoughts
Activity
d-v-b commentedon Jul 16, 2025
Why is it important that strings be identical? Since you know the strings should be interpreted as JSON, then any equality comparison should be between JSON objects, not strings.
TomNicholas commentedon Jul 16, 2025
We know, but the code doesn't. The partially-string versions are also valid JSON.
What you're suggesting is I guess what generally happens in practice. But it's papering over the fundamental ambiguity of the partially-string representation by expecting client code to know it has to do special steps to deserialize the kerchunk references, instead of just being able to treat them as actual JSON upon encountering them.
But this does mean there is a 5th option: have virtualizarr deserialize the entire structure to actual JSON-all-the-way-down as intended before doing any equality comparisons. Someone else who writes software that reads Kerchunk references will still have to realise the same ambiguity exists and deal with it the same way. But your suggestion might be the best option for us now.
TomNicholas commentedon Jul 16, 2025
In #680 I implemented option (5).
martindurant commentedon Jul 16, 2025
Seems like you've already sorted this out. I'll just comment that the JSON strings are usually just the ones that zarr (v2 originally) would make. The comparisons made in kerchunk's own tests are that the arrays produced from them have the correct attributes.