Newton.Json中JsonConverter的使用

一、使用场景:

有两个类都继承了同一个抽象类:

/// <summary>
/// 沙盒基类
/// </summary>
abstract class SandBoxGanmeBase
{
    /// <summary>
    /// 名称
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// 版本
    /// </summary>
    public string Version { get; set; }

    /// <summary>
    /// 可以驾驶
    /// </summary>
    public bool CanDrive { get { return true; } }
}

/// <summary>
/// GTA
/// </summary>
class GTA5 : SandBoxGanmeBase
{
    public bool HasHotCoffe { get; set; }
}

/// <summary>
/// Cyberpunk2077
/// </summary>
class Cyberpunk2077 : SandBoxGanmeBase
{
    public bool CanModifyDingDing { get { return true; } }
}

把信息保存入json:

string sJsonFile = AppDomain.CurrentDomain.BaseDirectory + "games.json";

IList<SandBoxGanmeBase> games = new List<SandBoxGanmeBase>();

games.Add(new GTA5()
{
    Name = "GTA5",
    Version = "1.53",
    HasHotCoffe = true
});

games.Add(new Cyberpunk2077()
{
    Name = "Cyberpunk2077",
    Version = "1.06"
});

string sJson = JsonConvert.SerializeObject(games, Formatting.Indented);

if (!File.Exists(sJsonFile))
    File.Create(sJsonFile).Close();

using (TextWriter chsTw = new StreamWriter(sJsonFile))
{
    chsTw.WriteLine(sJson);
chsTw.Flush();
}

games.json 内容:

 一般的,使用如下代码读取 games.json:

 string sJsonFile = AppDomain.CurrentDomain.BaseDirectory + "games.json";

 string sJson = string.Empty;
 using (StreamReader sr = new StreamReader(sJsonFile))
     sJson = sr.ReadToEnd();

 IList<SandBoxGanmeBase> games = JsonConvert.DeserializeObject<IList<SandBoxGanmeBase>>(sJson);

会 catch ex 报错:

因为,抽象类在 Newton.Json 反序列化时,无法确定继承抽象类的实例,因此,我们必须要告诉 Newton.Json:你需要使用哪个类来进行反序列化,因此,JsonConverter 应运而生。

 

二、自定义 JsonConverter

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            var jsonObject = JObject.Load(reader);
            object target = null;
            JToken gameName;
            if (jsonObject.TryGetValue("Name", out gameName))
            {
                switch (gameName.ToString())
                {
                    case "GTA5":
                        target = new GTA5();
                        break;
                    case "Cyberpunk2077":
                        target = new Cyberpunk2077();
                        break;
                }
            }
            serializer.Populate(jsonObject.CreateReader(), target);
            return target;
        }
        catch (Exception ex)
        {
            throw new Exception("解析异常:" + ex.Message);
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {

    }
}

修改读取 games.json 代码为:

string sJsonFile = AppDomain.CurrentDomain.BaseDirectory + "games.json";

string sJson = string.Empty;
using (StreamReader sr = new StreamReader(sJsonFile))
    sJson = sr.ReadToEnd();

IList<SandBoxGanmeBase> games = JsonConvert.DeserializeObject<IList<SandBoxGanmeBase>>(sJson, new MyJsonConverter());

反序列化正常:

 

三、拓展----用特性方式使用 JsonConverter

1、反序列化

SandBoxGanmeBase 增加特性,修改为:

/// <summary>
/// 沙盒基类
/// </summary>
[JsonConverter(typeof(MyJsonConverter))]
abstract class SandBoxGanmeBase
{
    ...
}

读取 games.json 方法还原为:

...

IList<SandBoxGanmeBase> games = JsonConvert.DeserializeObject<IList<SandBoxGanmeBase>>(sJson);

2、序列化

仅为 SandBoxGanmeBase 增加了特性,再进行序列化时,序列化的 Json 字符串为空,修改 MyJsonConverter,增加 CanWrite 属性:

class MyJsonConverter : JsonConverter
{
    ...

public
override bool CanWrite { get { return false; } } ...

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { } }

这样,在序列化对象时,就使用 Newton.Json 默认的 WriteJson 而不是使用 MyJsonConvert 中重写的 WriteJson。

 

这应该是2020年最后一篇博客记录了,2021年,一如既往,不要在小问题上重复犯错!

posted @ 2021-03-10 13:55  moon3  阅读(692)  评论(0编辑  收藏  举报