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

I wrote a custom JsonConverter that I hope will allow me to serialize and deserialize Encoding

objects in 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 called DeserializeObject

, and the method is ReadJson

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

      

Exception message:

The System.Text.Encoding target type is not a value type or an abstract class.

Did I miss something?

+3


source to share


2 answers


There are three problems I see with your converter.

  • You are using the wrong check in CanConvert()

    .
  • You are using the wrong name for the Encoding

    .
  • You are using the wrong method to get the value from the reader when deserializing.

Let them take this data at a time.

First, in your method, CanConvert

you use objectType.IsSubclassOf(typeof(Encoding))

to determine if the converter should handle Encoding

. This works great on serialization because you have a specific encoding instance (for example UTF8Encoding

) that is indeed a subclass Encoding

. However, when deserializing, the deserializer does not know what specific type of encoding you are going to do, so the type passed to the converter is equal to everything Encoding

. Since Encoding

it is not a subclass of itself, it CanConvert

returns false and your method is ReadJson

never called. This leaves Json.Net trying to instantiate itself Encoding

, which it cannot do (sinceEncoding

is abstract), so it throws the error mentioned in your question. Use typeof(Encoding).IsAssignableFrom(objectType)

inside your method instead CanConvert

.

Second, when serializing Encoding

internally, WriteJson

you output a property EncodingName

that is the encoding display name on screen, not the code page name. If you look at the documentation for the method Encoding.GetEncoding(string)

, it says:

Parameters

name

Type: System.String

Preferred encoding code page name. 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 section.



So, you must output the property value WebName

to your method WriteJson

if you want to use that value to later restore Encoding

to ReadJson

.

Third, in your method ReadJson

you are using reader.ReadAsString()

to try and get the encoding name from the JSON. This will not work the way you expect. When ReadJson

Json.Net is called, the reader is already positioned with the current value. When you call ReadAsString()

, it advances the reader to the next token and then tries 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 property Value

. Since it Value

has a type object

, you will need to pass 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

+1


source


Try:

    public class CustomConverter : JsonConverter
    {

        public override bool CanConvert(System.Type objectType)
        {
            return true;// objectType.IsAssignableFrom(typeof(Encoding));
        }

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

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var t = (Test)value;
            var e = (Encoding)t.MyProperty;
            writer.WriteValue(e.BodyName);
            //serializer.Serialize(writer, e.BodyName);
        }
    }

      



And in Main

:

            var o = new Test { MyProperty = Encoding.UTF8 };
            var s = new JsonSerializerSettings
            {
                Converters = new[] { new CustomConverter() }
            };
            var v = JsonConvert.SerializeObject(o, s);
            var o2 = new Test();
            o2.MyProperty = Encoding.GetEncoding(JsonConvert.DeserializeObject(v, s).ToString());

      

-1


source







All Articles