simonw/condense-json
GitHub: simonw/condense-json
一个通过替换字符串机制压缩和还原 JSON 数据的 Python 库,旨在减少包含重复文本的 JSON 体积。
Stars: 6 | Forks: 3
# condense-json
[](https://pypi.org/project/condense-json/)
[](https://github.com/simonw/condense-json/actions/workflows/test.yml)
[](https://github.com/simonw/condense-json/releases)
[](https://github.com/simonw/condense-json/blob/main/LICENSE)
使用替换字符串来压缩 JSON 的 Python 函数
## 安装
使用 `pip` 安装此库:
```
pip install condense-json
```
## 用法
`condense_json` 函数会在类似 JSON 的对象中搜索包含指定替换子字符串的字符串。它将这些子字符串替换为紧凑的表示形式,从而使 JSON 更加简洁。`uncondense_json` 函数则会反转此过程。
**`condense_json(obj: JSONInput, replacements: Mapping[str, Optional[str]]) -> Any`**
* **`obj`**:要压缩的 JSON 值 - 可以是字典、列表、字符串、数字、布尔值和 `None` 的任意嵌套组合。顶层的列表和字符串同样适用,不仅限于字典。
* **`replacements`**:一个映射,其键是替换 ID(例如 "1"、"2"),值是它们所代表的字符串。值为空(`None` 或 `""`)的条目将被忽略。
`JSONInput` 是一个递归类型别名,涵盖了所有可以用 JSON 表示的内容。它由协变容器类型构建,因此可以接受像 `dict[str, str]` 这样的窄类型值,而无需任何额外的注解。结果类型为 `Any`,因此可以对其进行索引、迭代和序列化,而无需进行类型收窄。
```
JSONInput = Union[
str, int, float, bool, None, "Sequence[JSONInput]", "Mapping[str, JSONInput]"
]
```
该函数返回输入 `obj` 的修改版本,其中匹配的子字符串已被替换。如果一个字符串*完全*由一个替换字符串组成,它将被替换为 `{"$": replacement_id}`。如果一个字符串包含一个或多个替换字符串,它将被替换为 `{"$r": [ ...segments...]}`,其中 segments 是原始字符串的部分内容和替换 ID。
匹配是通过从左到右扫描来找到的。当替换子字符串发生重叠时(例如 `"quick"` 和 `"quick brown fox"`),最长的匹配项优先,而不管 `replacements` 字典的顺序如何,因此对于等效的输入,输出是确定性的。
**示例:**
```
from condense_json import condense_json
input_json = {
"foo": {
"bar": {
"string": "This is a string with foxes in it",
"nested": {
"more": ["Here is a string", "another with foxes in it too"]
},
}
}
}
replacements = {"1": "with foxes in it"}
condensed_output = condense_json(input_json, replacements)
print(condensed_output)
# 预期输出:
# {
# "foo": {
# "bar": {
# "string": {"$r": ["This is a string ", {"$": "1"}]},
# "nested": {
# "more": [
# "Here is a string",
# {"$r": ["another ", {"$": "1"}, " too"]}
# ]
# }
# }
# }
# }
```
**`uncondense_json(obj: JSONInput, replacements: Mapping[str, Optional[str]]) -> Any`**
* **`obj`**:已压缩的 JSON 值。
* **`replacements`**:用于压缩的同一个 `replacements` 映射。
该函数会反转 `condense_json` 操作。它会查找 `{"$": replacement_id}` 和 `{"$r": [ ...segments...]}` 结构,并将它们替换为 `replacements` 字典中的原始字符串。
**示例:**
```
from condense_json import uncondense_json, condense_json # Import both
original = {
"sentence": "The quick brown fox jumps over the lazy dog",
"nested": {"list": ["fast fox", "lazy dog", "just some text"]},
}
replacements = {"1": "quick brown fox", "2": "lazy dog"}
condensed = condense_json(original, replacements)
uncondensed = uncondense_json(condensed, replacements)
assert uncondensed == original
```
如果输入到 `uncondense_json` 的 `obj` 不包含任何压缩结构,它将原样返回输入。
`uncondense_json` 非常严格:如果压缩后的输入格式不正确,它会抛出 `condense_json.UncondenseError`(`ValueError` 的子类),而不是悄无声息地生成损坏的输出。这涵盖了引用了在 `replacements` 中缺失的替换 ID 的标记(或者引用了值为空的标记,而 `condense_json` 从不为此生成标记),不是列表的 `$r` 值,以及不是字符串或 `{"$": id}` 字典的 `$r` segments。
```
from condense_json import uncondense_json, UncondenseError
try:
uncondense_json({"query": {"$": "gt"}}, {"1": "with foxes in it"})
except UncondenseError as ex:
print(ex) # Unknown replacement id: 'gt'
```
### `$`、`$r` 和 `$raw` 键的转义
压缩格式赋予了带有 `$` 或 `$r` 键的单键字典特殊的含义。如果你的输入数据已经包含这种形状的字典(例如 `{"price": {"$": "100"}}`),它们在解压缩时可能会被误解。
为了防止这种情况,`condense_json` 会对所有唯一键为 `$`、`$r` 或 `$raw` 的单键字典进行转义,将其包裹在 `{"$raw": ...}` 中:
```
from condense_json import condense_json, uncondense_json
original = {"price": {"$": "100"}}
condensed = condense_json(original, {"1": "with foxes"})
# {'price': {'$raw': {'$': '100'}}}
assert uncondense_json(condensed, {"1": "with foxes"}) == original
```
`uncondense_json` 会精确移除一层 `$raw` 包裹,并恢复其内容,且不会将其解释为标记。由于 `$raw` 本身也是以相同方式转义的,所以即使你的数据中已经包含 `$raw` 键,这也能正常工作;此外,先执行 `condense_json` 随后执行 `uncondense_json` 的往返操作始终是无损的——即使在多次应用时也是如此。
## 开发
要为这个库做贡献,请检出代码并使用 `uv run pytest` 运行测试:
```
cd condense-json
uv run pytest
```
标签:JSON, Python, SOC Prime, 压缩优化, 安全规则引擎, 开发工具, 无后门, 逆向工具