zoukankan      html  css  js  c++  java
  • 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;
            }
        }
    }
    实用封装
  • 相关阅读:
    bzoj3280
    bzoj3876
    洛谷 p1625
    bzoj1407
    bzoj1227
    bzoj1477 && exgcd学习笔记
    bzoj1345
    c#程序的config文件问题
    思维角度的重要性
    python异步初步窥探
  • 原文地址:https://www.cnblogs.com/ogurayui/p/13791891.html
Copyright © 2011-2022 走看看