Newtonsoft.Json解析不正确的json

时间:2016-10-09 11:58:49

标签: c# json json.net

我遇到了一个奇怪的问题:如果这样的字符串{"text":"s","cursorPosition":189,"dataSource":"json_northwind",不是正确的json,它仍然会被成功解析。

这是班级:

public class CompletionDataRequest
{
    public CompletionDataRequest(string text, int cursorPosition, string dataSource, string project)
    {
        Text = text;
        CursorPosition = cursorPosition;
        DataSource = dataSource;
        Project = project;
    }

    public string Text { get; }
    public int CursorPosition { get; }
    public string DataSource { get; }
    public string Project { get; }
}

这是令人惊讶的成功的测试:

var s = @"{""text"":""s"",""cursorPosition"":189,""dataSource"":""json_northwind"",";
var request = JsonConvert.DeserializeObject<CompletionDataRequest>(s);
request.Text.Should().Be("s");
request.CursorPosition.Should().Be(189);
request.DataSource.Should().Be("json_northwind");
request.Project.Should().BeNull();

库是否有一些松散的解析规则,或者这可能是一个错误?我是图书馆版本9.0.1

1 个答案:

答案 0 :(得分:5)

<强>更新

此问题已打开问题Deserializing unclosed object succeeds when the object has a parameterized constructor. #1038。它已在变更集Json.NET release 10.0.1中的0721bd4中修复。

原始答案

您在Json.NET中发现了一个错误。只有在使用参数化构造函数构造对象时才会出现它。如果我修改您的对象以具有非参数化构造函数:

public class CompletionDataRequest
{
    public CompletionDataRequest(string text, int cursorPosition, string dataSource, string project)
    {
        Text = text;
        CursorPosition = cursorPosition;
        DataSource = dataSource;
        Project = project;
    }

    [JsonConstructor]
    private CompletionDataRequest()
    {
    }

    [JsonProperty]
    public string Text { get; private set; }
    [JsonProperty]
    public int CursorPosition { get; private set; }
    [JsonProperty]
    public string DataSource { get; private set; }
    [JsonProperty]
    public string Project { get; private set; }
}

然后Json.NET将正确抛出JsonSerializationException

错误的原因如下。使用无参数构造函数创建对象时,Json.NET首先构造对象,然后使用JsonSerializerInternalReader.PopulateObject()填充它。此方法具有以下(简化)逻辑:

    private object PopulateObject(object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, string id)
    {
        bool finished = false;
        do
        {
            switch (reader.TokenType)
            {
                case JsonToken.PropertyName:
                {
                    // Read and process the property.
                }
                case JsonToken.EndObject:
                    finished = true;
                    break;
                case JsonToken.Comment:
                    // ignore
                    break;
                default:
                    throw JsonSerializationException.Create(reader, "Unexpected token when deserializing object: " + reader.TokenType);
            }
        } while (!finished && reader.Read());

        if (!finished)
        {
            ThrowUnexpectedEndException(reader, contract, newObject, "Unexpected end when deserializing object.");
        }

        return newObject;
     }

如您所见,有逻辑if (!finished)来验证对象实际上是否已关闭。

但是,在使用参数化构造函数创建对象时,使用JsonSerializerInternalReader.ResolvePropertyAndCreatorValues()在构造对象之前读取属性:

    private List<CreatorPropertyContext> ResolvePropertyAndCreatorValues(JsonObjectContract contract, JsonProperty containerProperty, JsonReader reader, Type objectType)
    {
        List<CreatorPropertyContext> propertyValues = new List<CreatorPropertyContext>();
        bool exit = false;
        do
        {
            switch (reader.TokenType)
            {
                case JsonToken.PropertyName:
                    // Read and process the property.
                    break;
                case JsonToken.Comment:
                    break;
                case JsonToken.EndObject:
                    exit = true;
                    break;
                default:
                    throw JsonSerializationException.Create(reader, "Unexpected token when deserializing object: " + reader.TokenType);
            }
        } while (!exit && reader.Read());

        return propertyValues;
    }

正如您所看到的那样,exit没有等效检查。

为此打开了一个问题Deserializing unclosed object succeeds when the object has a parameterized constructor. #1038