Skip to content

Commit 230c684

Browse files
committed
feat: add encode_json and loads helpers for better JSON null support
1 parent 9086144 commit 230c684

3 files changed

Lines changed: 86 additions & 2 deletions

File tree

src/toon_format/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,20 @@
2323
from .decoder import ToonDecodeError, decode
2424
from .encoder import encode
2525
from .types import DecodeOptions, Delimiter, DelimiterKey, EncodeOptions
26-
from .utils import compare_formats, count_tokens, estimate_savings
26+
from .utils import (
27+
compare_formats,
28+
count_tokens,
29+
encode_json,
30+
estimate_savings,
31+
loads,
32+
)
2733

2834
__version__ = "0.9.0-beta.1"
2935
__all__ = [
3036
"encode",
3137
"decode",
38+
"encode_json",
39+
"loads",
3240
"ToonDecodeError",
3341
"Delimiter",
3442
"DelimiterKey",

src/toon_format/utils.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
# __init__.py defines encode() before importing utils, so this is safe
3232
from . import encode
3333

34-
__all__ = ["count_tokens", "estimate_savings", "compare_formats"]
34+
__all__ = ["count_tokens", "estimate_savings", "compare_formats", "encode_json", "loads"]
3535

3636

3737
_TIKTOKEN_MISSING_MSG = (
@@ -40,6 +40,43 @@
4040
)
4141

4242

43+
def loads(json_string: str) -> Any:
44+
"""Parse JSON string into Python objects.
45+
46+
This is an alias for `json.loads()` provided for convenience and to ensure
47+
a TOON-friendly integration flow where JSON 'null' is correctly converted
48+
to Python 'None'.
49+
50+
Args:
51+
json_string: The JSON string to parse.
52+
53+
Returns:
54+
Any: Parsed Python data structure.
55+
"""
56+
return json.loads(json_string)
57+
58+
59+
def encode_json(json_string: str) -> str:
60+
"""Encode a JSON string directly into TOON format.
61+
62+
Parses the JSON string (converting 'null' to 'None' automatically)
63+
and then encodes the resulting Python object into TOON.
64+
65+
Args:
66+
json_string: The JSON string to encode.
67+
68+
Returns:
69+
str: TOON-formatted string.
70+
71+
Example:
72+
>>> import toon_format
73+
>>> toon_format.encode_json('{"abc": null}')
74+
'abc: null'
75+
"""
76+
data = loads(json_string)
77+
return encode(data)
78+
79+
4380
def _require_tiktoken():
4481
try:
4582
import tiktoken # type: ignore[import-not-found]

tests/test_json_integration.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import json
2+
from toon_format import encode_json, loads, encode
3+
4+
def test_loads_null_to_none():
5+
json_str = '{"abc": null, "xyz": 123}'
6+
data = loads(json_str)
7+
assert data["abc"] is None
8+
assert data["xyz"] == 123
9+
print("test_loads_null_to_none passed")
10+
11+
def test_encode_json_integration():
12+
json_str = '{"abc": null, "xyz": null}'
13+
# This should automatically handle null -> None -> TOON null
14+
toon_output = encode_json(json_str)
15+
expected = "abc: null\nxyz: null"
16+
assert toon_output.strip() == expected
17+
print("test_encode_json_integration passed")
18+
19+
def test_complex_json_integration():
20+
json_str = '''
21+
{
22+
"status": "success",
23+
"data": {
24+
"user": null,
25+
"items": [1, null, 3]
26+
}
27+
}
28+
'''
29+
toon_output = encode_json(json_str)
30+
assert "user: null" in toon_output
31+
# Check for null in items array (can be inline "1,null,3" or list "- null")
32+
assert "null" in toon_output
33+
print("test_complex_json_integration passed")
34+
35+
if __name__ == "__main__":
36+
test_loads_null_to_none()
37+
test_encode_json_integration()
38+
test_complex_json_integration()
39+
print("All JSON integration tests passed!")

0 commit comments

Comments
 (0)