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

Newtonsoft.JSON反序列化字符串化JSON schema报错原因及解决问询

问题描述

我从客户端接收ChatGPT API请求中代表函数的数据,原本打算先通过Newtonsoft.Json解析收到的JSON,再在解析过程中完成JSchema解析,但实际情况是JSchema解析先于常规JSON解析执行,而且[JsonIgnore]属性被JSchema解析忽略了。

核心问题

  • 为何处理循环中的json1会引发错误?
  • 为何JSchema解析器期望对象或布尔值,而ParametersJson的类型是字符串?
  • 是否存在可行方式,像json1那样编码JSON并正常反序列化,同时在客户端保持JSON schema为字符串格式?

报错信息

未处理的异常。Newtonsoft.Json.Schema.JSchemaReaderException: 读取schema时遇到意外令牌。预期StartObject、Boolean,实际得到String。路径'parameters',第7行,位置9。
在Newtonsoft.Json.Schema.Infrastructure.JSchemaReader.EnsureToken(JsonReader reader, String name, List`1 tokenTypes, String errorMessage)处

测试代码

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Schema;

public class Program
{
    public static void Main()
    {
        string json0 = @"
{
    'Name': 'TestFunction',
    'Description': 'This is a test function.',
    'parameters': 
    {
        'type': 'object', 
        'properties': 
        {
            'prop1': 
            {
                'type': 'string'
            }
        }
    }
}".Replace("'", "\"");
        string json1 = @"
{
    'Name': 'TestFunction',
    'Description': 'This is a test function.',
    'parameters': 
    '{
        'type': 'object', 
        'properties': 
        {
            'prop1': 
            {
                'type': 'string'
            }
        }
    }'
}".Replace("'", "\"");
        var arr = new[]{json0, json1};
        foreach (var json in arr)
        {
            Function deserializedFunction = JsonConvert.DeserializeObject<Function>(json);
            Console.WriteLine($"Name: {deserializedFunction.Name}");
            Console.WriteLine($"Description: {deserializedFunction.Description}");
            Console.WriteLine($"Parameters: {deserializedFunction.Parameters}");
            Console.WriteLine("-------------");
        }
    }
}

#nullable enable
public class Function
{
    /// <summary>
    /// 要调用的函数名称,只能包含a-z、A-Z、0-9、下划线和短横线,最大长度64
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// 函数功能描述,用于模型判断何时调用该函数
    /// </summary>
    public string? Description { get; set; }

    /// <summary>
    /// 函数接受的参数,以JSON Schema对象形式描述
    /// <br></br>
    /// 如果函数不需要参数,传入{"type": "object", "properties": { } }
    /// </summary>
    // 需要将客户端传来的字符串转换为JSchema对象
    [JsonIgnore]
    public JSchema Parameters { get; set; }

    [JsonProperty("parameters")]
    public string ParametersJson { get => Parameters.ToString(); set => Parameters = JSchema.Parse(value); }

    public Function(string name, string? description, JSchema parameters)
    {
        Name = name;
        Description = description;
        Parameters = parameters;
    }
}

问题解答

1. 处理json1报错的原因

json1的核心问题是JSON格式无效:
你通过Replace("'", "\"")替换单引号后,parameters字段的字符串值内部的双引号没有转义,导致JSON解析器无法正确识别完整的字符串内容。最终JSchema.Parse拿到的是一段损坏、不完整的文本,自然触发解析错误。

2. JSchema解析器期望对象或布尔值的原因

JSchema.Parse要求输入是合法的JSON Schema内容,而JSON Schema的合法格式只能是JSON对象(比如{"type":"object"})或布尔值(true表示允许任何值)。
在处理json1时,因为原始JSON格式错误,ParametersJson的setter拿到的是截断后的无效字符串,解析器无法将其识别为合法的JSON Schema结构,因此抛出"预期对象或布尔值"的错误。

3. 可行的解决方式

要实现客户端传JSON schema字符串、后端正常反序列化为JSchema,需要两步修正:

步骤1:确保客户端传递的JSON格式合法

客户端传递的parameters字符串必须正确转义内部双引号,比如:

// 修正后的json1,用双双引号实现转义,替换后会生成正确的\"转义符
string json1 = @"
{
    'Name': 'TestFunction',
    'Description': 'This is a test function.',
    'parameters': 
    '{
        ""type"": ""object"", 
        ""properties"": 
        {
            ""prop1"": 
            {
                ""type"": ""string""
            }
        }
    }'
".Replace("'", "\"");

步骤2:修正后端代码

添加无参构造函数(Newtonsoft.Json反序列化必需),并优化ParametersJson的set逻辑,或者使用自定义转换器更灵活地处理字符串与JSchema的转换:

方案A:优化原属性逻辑
#nullable enable
public class Function
{
    public string Name { get; set; }
    public string? Description { get; set; }

    [JsonIgnore]
    public JSchema Parameters { get; set; }

    [JsonProperty("parameters")]
    public string ParametersJson 
    { 
        get => Parameters.ToString(); 
        set 
        {
            if (!string.IsNullOrWhiteSpace(value))
            {
                Parameters = JSchema.Parse(value);
            }
        } 
    }

    // 添加无参构造函数,供Newtonsoft.Json反序列化使用
    public Function()
    {
        Name = string.Empty;
        Parameters = JSchema.Parse("{}");
    }

    public Function(string name, string? description, JSchema parameters)
    {
        Name = name;
        Description = description;
        Parameters = parameters;
    }
}
方案B:自定义JsonConverter(更灵活,支持对象/字符串两种输入)
// 自定义转换器,同时支持JSON对象和字符串格式的JSchema
public class JSchemaStringConverter : JsonConverter<JSchema>
{
    public override JSchema ReadJson(JsonReader reader, Type objectType, JSchema existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.String)
        {
            return JSchema.Parse((string)reader.Value);
        }
        else if (reader.TokenType == JsonToken.StartObject || reader.TokenType == JsonToken.Boolean)
        {
            return JSchema.Load(reader);
        }
        throw new JsonException("无法识别的JSchema格式");
    }

    public override void WriteJson(JsonWriter writer, JSchema value, JsonSerializer serializer)
    {
        writer.WriteValue(value.ToString());
    }
}

// 简化后的Function类
public class Function
{
    public string Name { get; set; }
    public string? Description { get; set; }

    [JsonProperty("parameters")]
    [JsonConverter(typeof(JSchemaStringConverter))]
    public JSchema Parameters { get; set; }

    public Function()
    {
        Name = string.Empty;
        Parameters = JSchema.Parse("{}");
    }

    public Function(string name, string? description, JSchema parameters)
    {
        Name = name;
        Description = description;
        Parameters = parameters;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 11:04:57