C#自定义Json序列化

鉴于网上的此类文章讲的不那么好,特在此重新讲一下

创建一个.Net Core控制台程序,本文代码需要Nuget包Newtonsoft。安装后就可以开始了
首先交代一下使用的类

public abstract class EntityBase
{
    public virtual long Id { get; set; }
}

public class Entity : EntityBase
{
    public override long Id { get; set; }
    public string Name { get; set; }
    [MyJsonIgnore]
    public decimal Money { get; set; }
    public Category Category { get; set; }
    public List<Child> Children { get; set; }
}

public class Child : EntityBase
{
    public string Name { get; set; }
}

public enum Category
{
    Human = 0,
    Cat = 1,
    Dog = 2
}
作为模型的类

默认情况下的序列化

public class Program
{
    public static void Main()
    {
        var entity = new Entity
        {
            Id = 11,
            Name = "aa",
            Money = 1000,
            Category = Category.Human,
            Children = new List<Child>
            {
                new Child{ Id = 22, Name = "bb"}
            }
        };

        var json = JsonConvert.SerializeObject(entity);
    }
}
Main代码

结果:


现在我们不想输出Id,并且Name换成"名字"

方案一:使用Newtonsoft的原生特性,适用于所有此类序列化输出都是相同的场景
主要特性

[JsonIgnore]:序列化成字符串时,不带上这个属性

[JsonProperty]:序列化时,修改属性名
如:作为模型的类修改如下

public abstract class EntityBase
{
    public virtual long Id { get; set; }
}

public class Entity : EntityBase
{
    [JsonIgnore]
    public override long Id { get; set; }
    [JsonProperty("名字")]
    public string Name { get; set; }
    public decimal Money { get; set; }
    public Category Category { get; set; }
    public List<Child> Children { get; set; }
}

public class Child : EntityBase
{
    [JsonProperty("名字")]
    public string Name { get; set; }
}

public enum Category
{
    Human = 0,
    Cat = 1,
    Dog = 2
}
作为模型的类

然后Main代码不用改

结果

这种方案的好处是简单,坏处就是只有1种输出

方案二:自己实现转化器convertor,适用于任何场景
1、在序列化时,Newtonsoft提供了自定义的方案,只要写一个类去继承Newtonsoft.Json.JsonConvertor类即可。

public class MyConvertor : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true; //反序列化时先执行
    }

    public override bool CanRead => false; //使用默认反序列化
    public override bool CanWrite => true;

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException(); //反序列化代码
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var jObject = new JObject();
        var entity = value as Entity;
        var type = typeof(Entity);
        var props = type.GetProperties();
        foreach (var prop in props)
        {
            var att = prop.GetCustomAttributes(typeof(MyJsonIgnoreAttribute), false).FirstOrDefault();
            if (att != null)
            {
                continue;
            }

            var name = prop.Name;
            var att2 = prop.GetCustomAttributes(typeof(MyJsonPropertyAttribute), false).FirstOrDefault();
            if (att2 != null)
            {
                name = ((MyJsonPropertyAttribute) att2).PropertyName;
            }

            var v = prop.GetValue(entity);
            jObject.Add(name, JToken.FromObject(v));
        }

        jObject.WriteTo(writer);
    }
}

[AttributeUsage(AttributeTargets.Property)]
public class MyJsonIgnoreAttribute : Attribute
{
}

[AttributeUsage(AttributeTargets.Property)]
public class MyJsonPropertyAttribute : Attribute
{
    public string PropertyName { get; }

    public MyJsonPropertyAttribute(string name)
    {
        PropertyName = name;
    }
}
Convertor类代码

注意1:本文只讨论序列化,不需要反序列化的场景。所以CanRead=false,并且CanConvert跟ReadJson都没有实现,如果想使用默认反序列化方案的,CanConvert返回true,CanConvert是表示使用此Convertor类反序列化时,模型对象能否反序列化,网上许多写法都返回了bool--“当前类是否继承某个类或实现某个接口来判断能否反序列化”,但我认为,既然是默认反序列化方案的话,直接返回true即可,除非你也自定义反序列化方案,那就要判断

public override bool CanConvert(Type objectType)
{
    return objectType.IsAssignableFrom(typeof(EntityBase));
}

注意2:在代码中,我使用了自定义的特性来代替原生的JsonIgnoreAttribute和JsonPropertyAttribute,如果你确定只可能有1种输出但又不想使用默认的序列化方案时,那么可以不自定义特性,用回原生特性(效果都一样)。使用自定义特性是为了当有其他的Convertor输出方案时,可以不一样。比如某个方案需要序列化json时,带上Id字段,某个方案又不带上,还有个方案要求Name显示成「名前」...em...一般极少2种及以上的情况的,所以极少需要自定义特性

注意3:在把生成的JObject对象写到writer中时,网上的博客大多数写法是这样的,各位看官可以验证这种写法最终序列化后会多出一次转义操作,这是错误的。stackoverflow网站上的写法也就是我这种jObject.WriteTo(writer);才是正确的

  

2、模型类使用特性,参考方案一那样

补充1:可以在模型类上,使用特性来绑定序列化Convertor,表明我的默认序列化方案就是这个Convertor;当JsonConvert.SerializeObject调用时不传convertor就是使用默认方案

[JsonConverter(typeof(MyConvertor))]

补充2:可以使用入参Formatting.Indented来使序列化的json换行

最后我选择继承泛型的JsonConvertor让更多的模型可以使用这个Convertor,如这里的Child类使用特性绑定了MyConvertor为默认序列化方案,全部代码整合如下:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Json
{
    public class Program
    {
        public static void Main()
        {
            var entity = new Entity
            {
                Id = 11,
                Name = "aa",
                Money = 1000,
                Category = Category.Human,
                Children = new List<Child>
                {
                    new Child{ Id = 22, Name = "bb"}
                }
            };

            var convertor = new MyConvertor<Entity>();
            var json = JsonConvert.SerializeObject(entity, Formatting.Indented, convertor);
        }
    }

    public abstract class EntityBase
    {
        public virtual long Id { get; set; }
    }

    public class Entity : EntityBase
    {
        [MyJsonIgnore]
        public override long Id { get; set; }
        [MyJsonProperty("なまえ")]
        public string Name { get; set; }
        public decimal Money { get; set; }
        public Category Category { get; set; }
        public List<Child> Children { get; set; }
    }

    [JsonConverter(typeof(MyConvertor<Child>))]
    public class Child : EntityBase
    {
        [MyJsonIgnore]
        public override long Id { get; set; }
        [JsonProperty("名字")]
        public string Name { get; set; }
    }

    public enum Category
    {
        Human = 0,
        Cat = 1,
        Dog = 2
    }

    public class MyConvertor<TEntity> : JsonConverter<TEntity>
    {
        public override bool CanRead => false; //使用默认反序列化
        public override bool CanWrite => true;

        public override TEntity ReadJson(JsonReader reader, Type objectType, TEntity existingValue, bool hasExistingValue,
            JsonSerializer serializer)
        {
            throw new NotImplementedException(); //反序列化代码
        }

        public override void WriteJson(JsonWriter writer, TEntity entity, JsonSerializer serializer)
        {
            var jObject = new JObject();
            var type = typeof(TEntity);
            var props = type.GetProperties();
            foreach (var prop in props)
            {
                var att = prop.GetCustomAttributes(typeof(MyJsonIgnoreAttribute), false).FirstOrDefault();
                if (att != null)
                {
                    continue;
                }

                var name = prop.Name;
                var att2 = prop.GetCustomAttributes(typeof(MyJsonPropertyAttribute), false).FirstOrDefault();
                if (att2 != null)
                {
                    name = ((MyJsonPropertyAttribute)att2).PropertyName;
                }

                var v = prop.GetValue(entity);
                jObject.Add(name, JToken.FromObject(v));
            }

            jObject.WriteTo(writer);
        }
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class MyJsonIgnoreAttribute : Attribute
    {
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class MyJsonPropertyAttribute : Attribute
    {
        public string PropertyName { get; }

        public MyJsonPropertyAttribute(string name)
        {
            PropertyName = name;
        }
    }
}
全部代码

其实特性的逻辑代码应该在特性的类里面写的,这里不搞这么复杂了,因为笔者的文章面向于初学者

运行结果

实用封装:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Json
{
    public class Program
    {
        public static void Main()
        {
            var entity = new Entity
            {
                Id = 11,
                Name = "aa",
                Money = 1000,
                Category = Category.Human,
                Children = new List<Child>
                {
                    new Child{ Id = 22, Name = "bb"},
                    new Child{ Id = 33, Name = "cc"}
                }
            };

            var convertor = new MyConvertor();
            var json = JsonConvert.SerializeObject(entity, Formatting.Indented, convertor);
        }
    }

    public abstract class EntityBase
    {
        public virtual long Id { get; set; }
    }

    public class Entity : EntityBase
    {
        [MyJsonIgnore]
        public override long Id { get; set; }
        [MyJsonProperty("なまえ")]
        public string Name { get; set; }
        public decimal Money { get; set; }
        public Category Category { get; set; }
        [MyJsonEntity(true)]
        public List<Child> Children { get; set; }
    }

    public class Child : EntityBase
    {
        [MyJsonIgnore]
        public override long Id { get; set; }
        [JsonProperty("名字")]
        public string Name { get; set; }
    }

    public enum Category
    {
        Human = 0,
        Cat = 1,
        Dog = 2
    }

    public class MyConvertor : JsonConverter
    {
        public override bool CanRead => false; //使用默认反序列化
        public override bool CanWrite => true;

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var jObject = new JObject();
            var type = value.GetType();
            var props = type.GetProperties();
            foreach (var prop in props)
            {
                var att = prop.GetCustomAttributes(typeof(MyJsonIgnoreAttribute), false).FirstOrDefault();
                if (att != null)
                {
                    continue;
                }

                var name = prop.Name;
                var att2 = prop.GetCustomAttributes(typeof(MyJsonPropertyAttribute), false).FirstOrDefault();
                if (att2 != null)
                {
                    name = ((MyJsonPropertyAttribute)att2).PropertyName;
                }

                var v = prop.GetValue(value);
                if (v == null)
                {
                    jObject.Add(name, null);
                    continue;
                }

                var att3 = prop.GetCustomAttributes(typeof(MyJsonEntityAttribute), false).FirstOrDefault();
                if (att3 != null)
                {
                    var a3 = (MyJsonEntityAttribute)att3;
                    if (a3.IsList)
                    {
                        var vs = (IList)v;
                        var tempV = new List<object>();
                        foreach (var t in vs)
                        {
                            var temp = JsonConvert.SerializeObject(t, this);
                            tempV.Add(JsonConvert.DeserializeObject(temp));
                        }

                        v = tempV;
                    }
                    else
                    {
                        var temp = JsonConvert.SerializeObject(v, this);
                        v = JsonConvert.DeserializeObject(temp);
                    }
                }

                jObject.Add(name, JToken.FromObject(v));
            }

            jObject.WriteTo(writer);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override bool CanConvert(Type objectType)
        {
            return true;
        }
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class MyJsonIgnoreAttribute : Attribute
    {
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class MyJsonPropertyAttribute : Attribute
    {
        public string PropertyName { get; }

        public MyJsonPropertyAttribute(string name)
        {
            PropertyName = name;
        }
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class MyJsonEntityAttribute : Attribute
    {
        public bool IsList { get; }

        public MyJsonEntityAttribute(bool isList)
        {
            IsList = isList;
        }
    }
}
实用封装
原文地址:https://www.cnblogs.com/ogurayui/p/13791891.html