Newtonsoft.Json - Getting corresponding line numbers of deserialized objects from JSON for better error handling

只谈情不闲聊 提交于 2020-03-21 18:04:20

问题


My application accepts long JSON templates from clients that I deserialize and process. I would like to provide better error handling information that contains lineNumber of invalid objects in the JSON text to customers. Note that this is for errors that occur in post-processing and NOT for errors that occur during deserialization as this is already handled by the Newtonsoft.

As an example, I have the below JSON and its corresponding .Net type

{
    "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; }
}

I would like the LineNumber and LinePosition properties to be populated at deserialization so that it may be used at a later time. I am currently deserializing the JSON using the below code

JsonConvert.DeserializeObject<RootObject>(value: rawJson,settings: mysettings);

Appreciate any responses


回答1:


I was able to get around this by implementing a custom converter like below. Any class that implements JsonLineInfo will automatically get the line number info for itself and its properties when it is deserialized.

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;
    }
}


来源:https://stackoverflow.com/questions/26050036/newtonsoft-json-getting-corresponding-line-numbers-of-deserialized-objects-fro

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!