KernelRanch
返回文章列表

JSON 校验前值就变了:在解析边界拒绝重复字段

2026-11-13 #json#python#api#validation

导入接口收到同一个键两次,解析结果只留下后一个值。下游 schema 校验时,原来那次字段出现已从对象里消失。要追查这类数据,需要在把键值列表折叠成字典之前检查重复项。

1. 在解析器里保留字段出现顺序

保存为 strict_json_lab.py,执行 python strict_json_lab.py。

import json
from decimal import Decimal

def reject_duplicates(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise ValueError(f"duplicate JSON key: {key!r}")
        result[key] = value
    return result

def reject_constant(token):
    raise ValueError(f"invalid JSON number: {token}")

def parse_document(text):
    return json.loads(text, object_pairs_hook=reject_duplicates,
                      parse_constant=reject_constant, parse_float=Decimal)

ambiguous = '{"order":{"amount":1,"amount":2}}'
assert json.loads(ambiguous)["order"]["amount"] == 2
print("default parser nested amount:", json.loads(ambiguous)["order"]["amount"])
for text, expected in [
    (ambiguous, "duplicate JSON key"),
    (r'{"x":1,"\u0078":2}', "duplicate JSON key"),
    ('{"amount":NaN}', "invalid JSON number"),
]:
    try:
        parse_document(text)
    except ValueError as error:
        assert str(error).startswith(expected)
        print(str(error))
    else:
        raise AssertionError(text)

valid = parse_document('{"amount":0.10}')
assert valid == {"amount": Decimal("0.10")}
print("valid decimal:", valid["amount"])

本地实测输出如下。

default parser nested amount: 2
duplicate JSON key: 'amount'
duplicate JSON key: 'x'
invalid JSON number: NaN
valid decimal: 0.10

2. 解析、字段校验和业务规则各管一层

层级可以捕获的问题
HTTP 入口请求过大、媒体类型错误
JSON 解析文本残缺、重复键、NaN 和 Infinity
结构校验缺字段、未知字段、类型不符
业务校验金额越界、对象所属租户不符

RFC 8259 建议对象内名称唯一,不同实现面对重复键的行为可能不同。本例把拒绝定为 API 策略,键在转义解码后比较,所以 x 和 Unicode 转义写出的 x 冲突。若服务还有 Unicode 搜索键规则,应在字段含义明确后另行应用。

parse_float=Decimal 只改变非整数数字的解析类型。金额需要单独校验位数和范围,序列化时也要明确输出契约。数据较大时,先限制输入字节数和可接受的嵌套程度,避免把整份异常内容写入日志。

小结

重复键检查放在 JSON 解析阶段。测试原始请求体而非预先构造的字典,并覆盖嵌套对象及转义后重复的成员。

数据来源与检索时间

检索时间 2026-11-13。

来源URL对应内容
RFC 8259https://www.rfc-editor.org/rfc/rfc8259.html对象成员名称与数字语法
Python JSONhttps://docs.python.org/3.11/library/json.html对象钩子、默认重复键处理和 parse_constant

核验说明

  • Python 3.11.9 验证嵌套字段覆盖、转义键重复、非标准数字和十进制解析。示例仅覆盖解析层。