0

I have the following custom JsonConverter:

using Microsoft.CodeAnalysis.Text;
using Newtonsoft.Json;
using System;

namespace CSTool.Json
{
    public class TextSpanJsonConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType) => objectType == typeof(TextSpan);

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var str = reader.ReadAsString();
            var delim = str.IndexOf('.');
            var start = int.Parse(str.AsSpan(1, delim - 1));
            var end = int.Parse(str.AsSpan(delim + 2, str.Length - delim - 3));
            return TextSpan.FromBounds(start, end);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            writer.WriteValue(value.ToString());
        }
    }
}

It is supposed to help (de)serialize the following class:

using CSTool.Json;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Newtonsoft.Json;

namespace CSTool.ObjectModel
{
    public class RefLocCacheItem
    {
        public string FilePath { get; private set; }
        [JsonConverter(typeof(TextSpanJsonConverter))]
        public TextSpan TextSpan { get; private set; }
        [JsonConverter(typeof(LinePositionSpanJsonConverter))]
        public LinePositionSpan LinePositionSpan { get; private set; }

        public RefLocCacheItem()
        {
        }
        public RefLocCacheItem(Location o) : this(o.SourceTree.FilePath, o.SourceSpan, o.GetLineSpan().Span)
        {
        }
        public RefLocCacheItem(string filePath, TextSpan textSpan, LinePositionSpan linePositionSpan)
        {
            FilePath = filePath;
            TextSpan = textSpan;
            LinePositionSpan = linePositionSpan;
        }
    }
}

The deserialization code is:

cached = JsonConvert.DeserializeObject<Dictionary<uint, List<RefLocCacheItem>>>(File.ReadAllText(cacheFile));

The respective serialization code is:

File.WriteAllText(cacheFile, JsonConvert.SerializeObject(cached, Newtonsoft.Json.Formatting.Indented));

And here is a sample json file:

{
  "100666494": [],
  "100666517": [],
  "67111627": [
    {
      "FilePath": "c:\xyz\\tip\\MySourceFile.cs",
      "TextSpan": "[105331..105379)",
      "LinePositionSpan": "(2379,51)-(2379,99)"
    }
  ],
  "67111628": [
    {
      "FilePath": "c:\xyz\\tip\\MySourceFile.cs",
      "TextSpan": "[136762..136795)",
      "LinePositionSpan": "(2953,30)-(2953,63)"
    }
  ],
  "100666534": []
}

So, as you can see, serialization works fine. However, the deserialization code never invokes the converter's ReadJson function. In fact, it does not work at all! There is no failure, but the returned dictionary contains RefLocCacheItems with null file paths and empty text spans:

enter image description here

I used Json.Net many times in the past and I don't understand what am I doing wrong now.

I use the latest version - 13.0.1, but I checked a few older versions - same thing. So, it is my fault, but where?

Clarification edit

The FilePath property is not deserialized. And it has nothing to do with the converter. And the converter - the ReadJson method is not even called!

halfer
  • 19,824
  • 17
  • 99
  • 186
mark
  • 59,016
  • 79
  • 296
  • 580
  • I haven't debugged your code yet, but you probably want `reader.Value` not `reader.ReadAsString()`. For why see [this answer](https://stackoverflow.com/a/46025896/3744182) to [Custom JsonConverter not working when using JsonReader instead of JsonSerializer](https://stackoverflow.com/q/46024882/3744182). In fact this may be a duplicate, agree? – dbc Apr 06 '21 at 16:41
  • 1
    @dbc - Thanks for responding. The problem is that FilePath is not deserialized either! The `ReadJson` is not even called! – mark Apr 06 '21 at 16:44
  • 2
    All of your properties have private setters. Try adding `[JsonProperty]` to the properties, or else make them public. – Brian Rogers Apr 06 '21 at 16:44
  • Well once the `JsonReader` is mis-positioned all sorts values are going to get dropped. – dbc Apr 06 '21 at 16:44
  • @dbc - it is never called at all. – mark Apr 06 '21 at 16:46
  • @BrianRogers - damn, this is it. I was 100% sure it does not matter for Json.Net. Please, arrange as the answer so I could credit you. – mark Apr 06 '21 at 16:48
  • @dbc - now that it is called, I can see you were right too. But the true reason - private setters. I am still shocked, since I was sure it does not matter. – mark Apr 06 '21 at 16:51

1 Answers1

2

The problem is your properties have private setters. As such, Json.Net will treat them as read-only. You can either make the setter public, or, if you wish to keep the setters private, you can add a [JsonProperty] attribute to the property to tell Json.Net that it is OK to use the private setter:

[JsonProperty]
public string FilePath { get; private set; }

[JsonProperty, JsonConverter(typeof(TextSpanJsonConverter))]
public TextSpan TextSpan { get; private set; }
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300