Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

json.net

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

like image 773
TechSeek Avatar asked Sep 26 '14 00:09

TechSeek


People also ask

How do you handle JSON deserialization error?

To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal . NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object.

Is JSON serialized or Deserialized?

JSON is language independent and because of that, it is used for storing or transferring data in files. The conversion of data from JSON object string is known as Serialization and its opposite string JSON object is known as Deserialization.

Which is better Newtonsoft JSON or System text JSON?

Text. Json is much faster than the Newtonsoft. Json.

Is Newtonsoft JSON obsolete?

Newtonsoft. Json package is not provided by RestSharp, is marked as obsolete on NuGet, and no longer supported by its creator.


1 Answers

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;
    }
}
like image 141
TechSeek Avatar answered Sep 21 '22 07:09

TechSeek