From a16a4f284fd27940b6808a08864a3c6408371fb6 Mon Sep 17 00:00:00 2001 From: Charlie Tonneslan Date: Fri, 22 May 2026 18:23:52 -0400 Subject: [PATCH] Fix unflatten crashing on keys that contain a dollar sign Round-tripping a dict like `{"foo$bar": 5}` blows up: >>> from json_flatten import flatten, unflatten >>> unflatten(flatten({"foo$bar": 5})) ValueError: too many values to unpack (expected 2) flatten produces `{"foo$bar$int": "5"}` correctly, but unflatten then calls `rsplit("$", 2)` and gets back three pieces instead of two. The greedy split was the only thing wrong; a `rsplit("$", 1)` keeps the type suffix and leaves the rest of the key alone, so "foo$bar$int" becomes ("foo$bar", "int"). Added two regression cases to the parametrized test. Signed-off-by: Charlie Tonneslan --- json_flatten.py | 5 ++++- test_json_flatten.py | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/json_flatten.py b/json_flatten.py index c8acaa5..6b0acd6 100644 --- a/json_flatten.py +++ b/json_flatten.py @@ -86,7 +86,10 @@ def unflatten(data): current = current[bit] # Now deal with $type suffixes: if _types_re.match(lastkey): - lastkey, lasttype = lastkey.rsplit("$", 2) + # rsplit on "$" once so a key that itself contains "$" (e.g. + # "foo$bar") round-trips intact: "foo$bar$int" -> ("foo$bar", + # "int") rather than crashing on a 3-element unpack. + lastkey, lasttype = lastkey.rsplit("$", 1) value = { "int": int, "float": float, diff --git a/test_json_flatten.py b/test_json_flatten.py index f4efb44..8b248e0 100644 --- a/test_json_flatten.py +++ b/test_json_flatten.py @@ -53,6 +53,15 @@ "foo.[0].phones._$!!$_": "555-555-5555", }, ), + # A key that contains "$" and has a typed value used to crash + # unflatten with `ValueError: too many values to unpack` because the + # type-suffix split was greedy and consumed the whole key. + ("dollar_sign_in_key_with_typed_value", {"foo$bar": 5}, {"foo$bar$int": "5"}), + ( + "multiple_dollar_signs_in_key_with_typed_value", + {"_$home$_": True}, + {"_$home$_$bool": "True"}, + ), ("empty_object", {}, {"$empty": "{}"}), ( "nested_empty_objects",