Newtonsoft.Json - 从JSON获取反序列化对象的相应行号,以便更好地处理错误

Tec*_*eek 5 c# json.net

我的应用程序接受来自我反序列化和处理的客户端的长JSON模板.我想向客户提供更好的错误处理信息,其中包含JSON文本中无效对象的lineNumber.请注意,这是针对后处理中发生的错误而不是针对反序列化期间发生的错误,因为这已经由Newtonsoft处理.

作为一个例子,我有以下JSON及其相应的.Net类型

{
    "Version": "1.0.0.0",                        
    "MyComplexObject": [
    {
        "Prop1": "Val1",
        "Prop2": "Val2",
        "Prop3": 1
    }
    ]
}

public class MyComplexObject
{
    [JsonProperty]
    public string Prop1 { get; set; }

    [JsonProperty]
    public string Prop2 { get; set; }

    [JsonProperty]
    public int Prop3 { get; set; }

    **public int LineNumber;
    public int LinePosition;**
}

public class RootObject
{
    [JsonProperty]
    public string Version { get; set; }

    [JsonProperty]
    public List<MyComplexObject> MyComplexObject { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我希望在反序列化时填充LineNumber和LinePosition属性,以便以后可以使用它.我目前正在使用以下代码反序列化JSON

JsonConvert.DeserializeObject<RootObject>(value: rawJson,settings: mysettings);
Run Code Online (Sandbox Code Playgroud)

感谢任何回复

Tec*_*eek 8

我可以通过实现如下所示的自定义转换器来解决这个问题。任何实现 JsonLineInfo 的类在反序列化时都会自动获取其自身及其属性的行号信息。

public class LineInfo
{
    [JsonIgnore]
    public int LineNumber { get; set;}

    [JsonIgnore]
    public int LinePosition { get; set;}        
}

public abstract class JsonLineInfo : LineInfo
{
    [JsonIgnore]
    public Dictionary<string, LineInfo> PropertyLineInfos { get; set; }
}

class LineNumberConverter : JsonConverter
{
    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException("Converter is not writable. Method should not be invoked");
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(JsonLineInfo).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType != JsonToken.Null)
        {
            int lineNumber = 0;
            int linePosition = 0;
            var jsonLineInfo = reader as IJsonLineInfo;
            if (jsonLineInfo != null && jsonLineInfo.HasLineInfo())
            {
                lineNumber = jsonLineInfo.LineNumber;
                linePosition = jsonLineInfo.LinePosition;
            }

            var rawJObject = JObject.Load(reader);
            var lineInfoObject = Activator.CreateInstance(objectType) as JsonLineInfo;
            serializer.Populate(this.CloneReader(reader, rawJObject), lineInfoObject);

            return this.PopulateLineInfo(
                lineInfoObject: lineInfoObject,
                lineNumber: lineNumber,
                linePosition: linePosition,
                rawJObject: rawJObject);
        }

        return null;
    }

    private JsonReader CloneReader(JsonReader reader, JObject jobject)
    {
        var clonedReader = jobject.CreateReader();

        clonedReader.Culture = reader.Culture;
        clonedReader.DateParseHandling = reader.DateParseHandling;
        clonedReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
        clonedReader.FloatParseHandling = reader.FloatParseHandling;
        clonedReader.MaxDepth = reader.MaxDepth;

        return clonedReader;
    }

    private object PopulateLineInfo(JsonLineInfo lineInfoObject, int lineNumber, int linePosition, JObject rawJObject)
    {
        if (lineInfoObject != null)
        {
            lineInfoObject.PropertyLineInfos = new Dictionary<string, LineInfo>(StringComparer.InvariantCultureIgnoreCase);
            lineInfoObject.LineNumber = lineNumber;
            lineInfoObject.LinePosition = linePosition;

            foreach (var property in rawJObject.Properties().CoalesceEnumerable())
            {
                var propertyLineInfo = property as IJsonLineInfo;
                if (propertyLineInfo != null)
                {
                    lineInfoObject.PropertyLineInfos.Add(
                        property.Name,
                        new LineInfo
                        {
                            LineNumber = propertyLineInfo.LineNumber,
                            LinePosition = propertyLineInfo.LinePosition
                        });
                }
            }
        }

        return lineInfoObject;
    }
}
Run Code Online (Sandbox Code Playgroud)