【问题标题】:How to enforce quotes on property names on JSON .NET如何在 JSON .NET 上对属性名称强制使用引号
【发布时间】:2018-11-14 15:59:27
【问题描述】:

我正在使用 Json.NET 将收到的一些 JSON 解析到我的应用程序上的端点。

我希望以下 JSON 对象无法解析,因为它的属性名称上没有引号:

{
  foo: "bar"
}

JToken.Parse() 表示它是有效的 JSON。但是,当我使用 online parser 时,出现以下错误

字符串应该用双引号括起来。

有没有办法让 JSON .NET 强制执行此规则?

【问题讨论】:

  • 该答案的问题在于它没有显示实施解决方案的适当方式。我查看了 JSON .NET 中的 JsonTextReader 类,但我不确定我应该做什么。我不敢相信像 JSON .NET 这样流行的库不能支持严格的解析。
  • 也相关:Unquoted json property name。那个从未得到回答,但评论指出,看起来不是那样的。我应该回答这个问题,并添加一些关于如何分叉JsonTextReader 来做你需要的事情的详细信息吗?
  • 那将非常有用,谢谢。
  • 已添加并更新答案。

标签: .net json json.net jsonparser


【解决方案1】:

Json.NET 目前没有实现对 JSON 属性名的严格解析。

JToken.Parse() 在内部构造了一个 JsonTextReader 来解析 JSON 字符串,目前似乎无法禁用 JsonTextReader 解析未加引号的属性名称的能力。

通过JsonTextReader.Read()遍历JSON文件时,使用JsonTextReader.ParseProperty()方法解析属性名:

Newtonsoft.Json.JsonTextReader.ParseUnquotedProperty() 
Newtonsoft.Json.JsonTextReader.ParseProperty() 
Newtonsoft.Json.JsonTextReader.ParseObject() 
Newtonsoft.Json.JsonTextReader.Read() 
Newtonsoft.Json.Linq.JContainer.ReadTokenFrom()

而且,从current reference source 中可以看出,此方法会自动处理双引号、单引号和未引号的属性:

private bool ParseProperty()
{
    char firstChar = _chars[_charPos];
    char quoteChar;

    if (firstChar == '"' || firstChar == '\'')
    {
        _charPos++;
        quoteChar = firstChar;
        ShiftBufferIfNeeded();
        ReadStringIntoBuffer(quoteChar);
    }
    else if (ValidIdentifierChar(firstChar))
    {
        quoteChar = '\0';
        ShiftBufferIfNeeded();
        ParseUnquotedProperty();
    }
    else
    {
        throw JsonReaderException.Create(this, "Invalid property identifier character: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
    }

    // REMAINDER OMITTED

如您所见,没有选项可以配置阅读器以对非双引号属性抛出异常。

作为一种解决方法current Json.NET license 允许复制和修改。因此,您应该能够创建自己的public class StricterJsonTextReader : JsonReader,复制自JsonTextReader,并修改ParseProperty(),如下所示:

private bool ParseProperty()
{
    char firstChar = _chars[_charPos];
    char quoteChar;

    if (firstChar == '"')
    {
        _charPos++;
        quoteChar = firstChar;
        ShiftBufferIfNeeded();
        ReadStringIntoBuffer(quoteChar);
    }
    else
    {
        // JsonReaderException.Create() is an internal static method,
        // so you will need to replace this with some extension method
        throw JsonReaderException.Create(this, "Invalid property identifier character: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
    }

但是,这可能不是一件容易的事,因为JsonTextReader 广泛使用了Src/Newtonsoft.Json/Utilities 目录中的实用程序。您应该预算几天的时间来制作必要实用程序的最小副本。

或者,您可以创建自己的 Json.NET 版本,自己构建,然后使用它来代替官方版本。无论哪种方式,请务必从您要使用的版本中分叉源代码:

作为创建自己的解析器的替代方法,您可以使用 JsonReaderWriterFactory.CreateJsonReader() 预处理您的 JSON,以确保严格遵守 JSON 标准:

public static class JsonExtensions
{
    public static JToken StrictParse(string json)
    {
        try
        {
            // Throw an exception if the json string is not in strict compliance with the JSON standard
            // by tokenizing it with the JSON reader used by DataContractJsonSerializer:
            using (var stream = GenerateStreamFromString(json))
            using (var reader = System.Runtime.Serialization.Json.JsonReaderWriterFactory.CreateJsonReader(stream, System.Xml.XmlDictionaryReaderQuotas.Max))
            {
                while (reader.Read())
                {
                }
            }
        }
        catch (Exception ex)
        {
            // Wrap the XmlException in a JsonReaderException
            throw new JsonReaderException("Invalid JSON", ex);
        }
        // Then actually parse with Json.NET
        return JToken.Parse(json);
    }

    static MemoryStream GenerateStreamFromString(string value)
    {
        return new MemoryStream(Encoding.UTF8.GetBytes(value ?? ""));
    }
}

(您需要为您的框架添加对适当 .Net 程序集的引用。)

性能会更差,因为您将有效地解析 JSON 两次,但实施工作很简单。

奇怪的是,我无法使用 JavaScriptSerializer 检查严格的 JSON 合规性,因为它也接受不带引号的属性名称!

// The following does not throw an exception:
new System.Web.Script.Serialization.JavaScriptSerializer().DeserializeObject("{foo : 'bar'}")

相关链接:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-31
    • 2020-12-27
    • 1970-01-01
    • 2019-09-05
    • 2015-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多