You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust Serde反序列化:如何确保字段不存在,存在则抛错?

问题

需要确保在反序列化时某些字段绝对不能被指定——只要字段本身存在(无论值是什么),就必须在反序列化过程中抛出错误(注意:不是字段值为None的场景,是字段不能出现在输入数据中)。

之前尝试的代码依赖deserializer.is_human_readable()判断,逻辑不合理:它仅在文本格式(如JSON/YAML)中报错,却允许二进制格式(如Postcard)中存在该字段,不符合需求:

#[derive(Deserialize)]
struct MyStruct {
    #[serde(default, deserialize_with = "check_field")]
    forbidden_field: Option<String>,
    other_field: String,
}

fn check_field<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
    D: Deserializer<'de>,
{
    if deserializer.is_human_readable() {
        Err(serde::de::Error::custom("Forbidden field should not be present"))
    } else {
        Ok(None)
    }
}

求通用、正确的实现方式。


解决方案

要实现「只要目标字段存在就报错」的需求,无论序列化格式是文本还是二进制,我们可以通过自定义Visitor拦截字段的反序列化流程——只要反序列化器尝试读取该字段,就直接返回错误。

实现代码

use serde::{Deserialize, Deserializer};
use serde::de::{Visitor, Error};
use std::fmt;

#[derive(Deserialize)]
struct MyStruct {
    // 指定自定义反序列化函数,拦截该字段的读取
    #[serde(deserialize_with = "reject_forbidden_field")]
    forbidden_field: (),
    other_field: String,
}

fn reject_forbidden_field<'de, D>(deserializer: D) -> Result<(), D::Error>
where
    D: Deserializer<'de>,
{
    // 自定义Visitor,所有方法均返回错误
    struct RejectFieldVisitor;

    impl<'de> Visitor<'de> for RejectFieldVisitor {
        type Value = ();

        // 描述预期(用于错误提示)
        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("this field must not be present in input")
        }

        // 针对所有基础数据类型,直接返回错误
        fn visit_bool<E>(self, _: bool) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }

        fn visit_i8<E>(self, _: i8) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }

        fn visit_i16<E>(self, _: i16) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }

        fn visit_i32<E>(self, _: i32) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }

        fn visit_i64<E>(self, _: i64) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }

        fn visit_u8<E>(self, _: u8) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }

        fn visit_u16<E>(self, _: u16) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }

        fn visit_u32<E>(self, _: u32) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }

        fn visit_u64<E>(self, _: u64) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }

        fn visit_f32<E>(self, _: f32) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }

        fn visit_f64<E>(self, _: f64) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }

        fn visit_char<E>(self, _: char) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }

        fn visit_str<E>(self, _: &str) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }

        fn visit_string<E>(self, _: String) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }

        // 处理空值、单元类型等复合场景
        fn visit_none<E>(self) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }

        fn visit_some<D>(self, _: D) -> Result<Self::Value, D::Error> where D: Deserializer<'de> {
            Err(D::Error::custom("forbidden field 'forbidden_field' must not exist"))
        }

        fn visit_unit<E>(self) -> Result<Self::Value, E> where E: Error {
            Err(E::custom("forbidden field 'forbidden_field' must not exist"))
        }
    }

    // 使用deserialize_any适配所有类型的反序列化器
    deserializer.deserialize_any(RejectFieldVisitor)
}

方案说明

  1. 类型选择:用()作为被禁止字段的类型,因为我们不需要存储该字段的任何值,仅需检测其是否存在。
  2. 通用拦截:自定义RejectFieldVisitor并为所有Visitor方法实现错误返回逻辑,确保无论字段值是什么类型,只要反序列化器尝试读取该字段就会触发错误。
  3. 格式无关:调用deserialize_any适配所有反序列化器类型,不管是JSON/YAML这类文本格式,还是Postcard/BSON这类二进制格式,都能生效。

如果你的需求是禁止所有未知字段,可以直接使用#[serde(deny_unknown_fields)]属性,但上述方案更适合禁止特定字段、允许其他未知字段的场景。


内容的提问来源于stack exchange,提问作者sudoExclamationExclamation

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 20:24:52