My problem was the serialize of the Encoding . Let’s suppose that we have a class that have a property Encoding( maybe to read a file ).
internal class MyTest
{
public MyTest()
{
enc = ASCIIEncoding.ASCII;
}
public Encoding enc { get; set; }
}
We want to serialize this class in order to let the administrator/people to decide what will be the encoding.
When we serialize( obvious, with NewtonSoftJson) , we obtain this kind of data:
{
“enc”: {
“IsSingleByte”: true,
“BodyName”: “us-ascii”,
“EncodingName”: “US-ASCII”,
“HeaderName”: “us-ascii”,
“WebName“: “us-ascii”,
“WindowsCodePage”: 1252,
“IsBrowserDisplay”: false,
“IsBrowserSave”: false,
“IsMailNewsDisplay”: true,
“IsMailNewsSave”: true,
“EncoderFallback”: {
“DefaultString”: “?”,
“MaxCharCount”: 1
},
“DecoderFallback”: {
“DefaultString”: “?”,
“MaxCharCount”: 1
},
“IsReadOnly”: true,
“CodePage”: 20127
}
}
This is too much for someone to edit . We want a simple string that can be edited easy – and the WebName , that is , in fact , a string from https://www.iana.org/assignments/character-sets/character-sets.xhtml seems the obvious choice.
So – I have done a JSONConverter class just for this property. It is very simple:
public class JsonEncodingConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (typeof(Encoding).IsAssignableFrom(objectType));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
string webName = "";
if (reader.TokenType == JsonToken.String)
{
webName = reader.Value?.ToString();
}
existingValue = Encoding.GetEncoding(webName);
return existingValue;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
string webName = (value as Encoding).WebName;
serializer.Serialize(writer, webName);
}
}
And can be used very easy:
MyTest m = new MyTest();
JsonEncodingConverter[] conv = new[] { new JsonEncodingConverter() };
string original = JsonConvert.SerializeObject(m, Formatting.Indented);
string data = JsonConvert.SerializeObject(m, Formatting.Indented, conv);
//https://www.iana.org/assignments/character-sets/character-sets.xhtml
Console.WriteLine(data);
Console.WriteLine("and now the original");
Console.WriteLine(original);
MyTest s = JsonConvert.DeserializeObject<MyTest>(data, conv);
Console.WriteLine(s.enc.WebName);
The result of serializing it is now
{
“enc”: “us-ascii”
}
And because it has this code
public override bool CanConvert(Type objectType)
{
return (typeof(Encoding).IsAssignableFrom(objectType));
}
it means it will serialize just the encoding, not other tools.
And it is more easy to be edited by someone.
Moral: Aim for simple string that can be edited can be achieved when serializing. Do not stay with defaults!
( you can easy achieve backwards compatibility for already serialized Encoding by asking
if (reader.TokenType == JsonToken.StartObject)
{
webName = reader.Value?.ToString();
//handling old data format for encoding
while (reader.TokenType != JsonToken.EndObject)
{
if (!reader.Read())
break;
if (reader.TokenType != JsonToken.PropertyName)
continue;
var val = reader.Value?.ToString();
if (string.Compare("webname", val, StringComparison.InvariantCultureIgnoreCase) == 0)
{
webName = reader.ReadAsString();
//do not break - advance reading to the end
//break;
}
}
}
)