Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a custom JsonConverter to handle System.Text.Encoding objects

I have written a custom JsonConverter which I am hoping will allow me to serialize and deserialize Encoding objects within my classes:

public class EncodingConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsSubclassOf(typeof(Encoding));
    }

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

    public override bool CanRead { get { return true; } }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var name = reader.ReadAsString();
        return Encoding.GetEncoding(name);
    }
}

However, when I run the following test code, I get an exception when calling DeserializeObject, and the ReadJson method never gets called.

class Program
{
    private static void Main(string[] args)
    {
        var test = new TestClass();

        var jsonSettings = new JsonSerializerSettings
        {
            Converters = new[] { new EncodingConverter(), }
        };

        var json = JsonConvert.SerializeObject(test, jsonSettings);

        var test2 = JsonConvert.DeserializeObject<TestClass>(json, jsonSettings);
    }
}

class TestClass
{
    public string Property1;
    public Encoding Encoding = Encoding.UTF8;
}

The exception message is:

Target type System.Text.Encoding is not a value type or a non-abstract class.

Am I missing something?

like image 925
user626528 Avatar asked Apr 29 '26 07:04

user626528


1 Answers

There are three problems with your converter that I see.

  1. You are using the wrong check in CanConvert().
  2. You are using the wrong name for the Encoding when serializing.
  3. You are using the wrong method to get the value from the reader when deserializing.

Let's take these one at a time.

First, in your CanConvert method you are using objectType.IsSubclassOf(typeof(Encoding)) to determine whether the converter should handle the Encoding. This works fine on serialization because you have a concrete instance of the encoding (e.g. UTF8Encoding), which is indeed a subclass of Encoding. However, on deserialization, the deserializer doesn't know what concrete type of encoding you are going to make, so the type that is passed to the converter is just Encoding. Since Encoding is not a subclass of itself, CanConvert returns false, and your ReadJson method never gets called. That leaves Json.Net to try to instantiate the Encoding itself, which it can't do (because Encoding is abstract), so it throws the error you mentioned in your question. You should instead use typeof(Encoding).IsAssignableFrom(objectType) inside your CanConvert method.

Second, when serializing the Encoding inside WriteJson, you are outputting the EncodingName property, which is the human-readable display name of the encoding, not the code page name. If you look at the documentation for the Encoding.GetEncoding(string) method, it says:

Parameters

name

Type: System.String

The code page name of the preferred encoding. Any value returned by the WebName property is valid. Possible values are listed in the Name column of the table that appears in the Encoding class topic.

So, you should be outputting the value of the WebName property in your WriteJson method if you want to be able to use this value to later reconstruct the Encoding in ReadJson.

Third, in your ReadJson method you are using reader.ReadAsString() to attempt to get the encoding name from the JSON. This will not work as you expect. When ReadJson is called by Json.Net, the reader is already positioned at the current value. When you call ReadAsString(), that advances the reader to the next token and then attempts to interpret that token as a string. What you really want to do is just get the value of the current token, which you can do using the Value property. Because Value is of type object, you will need to cast it to a string.

Here is the corrected code for the converter:

public class EncodingConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(Encoding).IsAssignableFrom(objectType);
    }

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return Encoding.GetEncoding((string)reader.Value);
    }
}

Fiddle: https://dotnetfiddle.net/UmLynX

like image 148
Brian Rogers Avatar answered May 01 '26 21:05

Brian Rogers



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!