Newtonsoft.Json使用

本文详细介绍了在Unity中如何使用Newtonsoft.Json,包括安装方法、通过DOM和反射序列化JSON,以及处理嵌套派生类体系的序列化。强调了在不同场景下选择合适序列化方式的重要性,并推荐了使用JsonConverter定制转换器。

Newtonsoft.Json

介绍

  • Newtonsoft.Json 另一个名称是 json.net

  • 官网 https://www.newtonsoft.com/json

  • github https://github.com/JamesNK/Newtonsoft.Json

  • 文档 https://www.newtonsoft.com/json/help/

  • 示例 https://www.newtonsoft.com/json/help/html/Samples.htm

  • 由于很多库都会用到 Newtonsoft.Json,而 unity 中不能有同名程序集,因此 unity 集成了自己的 Newtonsoft.Json 插件,
    可在 PackageManager 中查找 collab-proxy 安装,命名空间从 Newtonsoft.Json 改成 Unity.Plastic.Newtonsoft.Json
    建议你写的代码都使用这个插件,对于多个第3方都使用 Newtonsoft.Json,只能保留1个,强制删除多余的

安装

  1. 直接通过 unity 的 PackageManager 安装 collab-proxy
  2. 通过官网下载,里面包含源码和编译好的程序集
  3. 通过 github 克隆源码后自己编译

通过类似DOM序列化JSON

  • 类层次介绍

    • JToken 对象基类,可以放在 : 右边的,都是 JToken,包含类型和值
    • JProperty 表示一组 name:jtoken,有一个字符串的 Name 和 一个 JToken 的Value
    • JValue : JToken 原生值类型,比如 int,string 都会创建 JValue 来存放,原生值类型跟JToken可以隐式转换,会自动创建 JValue
      如果是原生类型,,则可以跟 JToken 直接互转,比如 int a = (int)jtoken; JToken jtoken=(JToken)a;
      如果是派生类型,一样可以互转包含原生类型,以及 JObject , JArray 等派生类型
    • JObject:JToken 表示 {},包含一组 JProperty
    • JArray:JToken 表示 [],包含一组 JToken
  • 示例代码

    public class Example
    {
        public static string JSON_OBJECT_STR = "{\"ID\":11,\"Name\":\"张三1\",\"Birthday\":\"2020-11-02T00:00:00\",\"IsVIP\":true}";    //  \" 表示 "
        public static string JSON_ARRAY_STR = @"[{""ID\"":11,""Name"":""张三1""},{""ID"":12,""Name"":""李四""}]";       //  用 @ 转义,则用 "" 表示 "
    
        public static void RemoveProperty(JObject obj)
        {
            if ( obj.Remove("ID") )
            {
    
            }
        }
    
        //  创建对象
        public static JObject CreateObject()
        {
            JObject obj = new JObject();
    
            obj.Add("ID", 11);
            obj.Add("Name", "张三1");
            obj.Add("Birthday", DateTime.Parse("2020-11-02"));
            obj.Add("IsVIP", true);
            obj.Add("Account", 13.34f);
            obj.Add("Remark", null);
    
            obj.Add(new JProperty("ID", 11));
        }
    
        //  用初始化器填充对象
        public static JObject CreateObject2()
        {
            return new JObject()
    		{
                { "name", "jack" },
                { "age", 28 }
    		};
        }
    
    
        public static JObject CreateObject3()
        {
            return new JObject(
                new JProperty("title", "James Newton-King"),
                new JProperty("price", 3.3),
                new JProperty("channel",  new JObject(
                    new JProperty("item", new JArray(
                        from p in posts
                        orderby p.Title
                        select new JObject(
                            new JProperty("title", p.Title),
                            new JProperty("description", p.Description),
                        )
                    ))
                ))
            );
        }
    
        public static JObject ParseObject1()
        {
            return JObject.Parse(JSON_OBJECT_STR);
        }
    
        public static JObject ParseObject2()
        {
            //  实体类直接转成 JObject
            Person person = new Person();
            return JObject.FromObject(person);
        }
    
        //  复用匿名对象快速创建 JObject
        public static JObject CreateObject5()
        {
            //  匿名实体类
            return JObject.FromObject(new { name = "jack", age = 28, data = new {title="1"} });
        }
    
        public static JArray CreateArray()
        {
            JArray array = new JArray();
            array.Add(new JValue("上班"));
            array.Add(new JValue("下班"));
            return array;
        }
    
        public static JArray CreateArray2()
        {
            return new JArray("上班", "下班"); 
        }
    
        public static JArray CreateArray3()
        {
            return JArray.Parse(JSON_ARRAY_STR);
        }
    
        public static JArray CreateArray4()
        {
            Person[] person = new Person[2];
    		return JArray.FromObject(person);
        }
    
        public static void WriteJsonFile()
        {
            JObject o = CreateObject();
    
    		//	直接写入文件
    		using (StreamWriter writer = File.CreateText("c:\\1.json"))
    		{
    			o.WriteTo(new JsonTextWriter(writer));
    		}
    
    		//	转成字符串再写入文件
    		string s = o.ToString();
    		File.WriteAllText("c:\\1.json", s);
        }
    
        //  解析 JToken        
        public static void ParseToken(JToken token)
        {
            if ( token == null )
                return;
    
            //  如果你知道类型,直接强转,对于值类型,已经重载了强转函数
            int i = (int)token;
            string s = (string)token;
            JObject o = (JObject)token;
            JArray a = (JArray)token;
    
            //  如果不知道类型,可以通过类型判断 
            if ( token.Type == JTokenType.Object )
            {}
        }
    
        //  解析 JObject
        public static void ParseObject(JObject obj)
        {
            //  几种获得属性的方法,底层实现都一样
            JToken j1 = obj["ID"];      //  获得属性 "ID" 的值
            JToken j2 = obj.Property("ID");
            JToken j3 = obj.GetValue("ID");
            if ( obj.TryGetValue(out JToken j4) ){}
    
            //  是否包含属性
            if ( obj.ContainsKey("ID") )
            {}
    
            //  遍历属性
            foreach (JProperty prop : obj.Properties())
            {
                string name = prop.Name;
                JToken value = prop.Value;
            }
    
            //  直接遍历属性的值
            foreach (JToken j3 : obj.PropertyValues())
            {
            }
    
            ParseToken(j1);
        }
    
        public static void ParseArray(JArray array)
        {
            //  遍历
            foreach (JToken jtoken in array)
            {}
    
            foreach (JToken jtoken in array.Children() )
            {}
    
            for ( int i=0; i<array.Count; i++ )
            {
                JToken jtoken = array[i];
            }
    
            //  直接转成具体类型
            foreach ( int i in array.Values<int>() )
            {}
        }
    
        public static void ReadJsonFile()
        {
    		//	直接解析文件
            using (StreamReader reader = File.OpenText(@"c:\person.json"))
            {
                JObject o = (JObject)JToken.ReadFrom(new JsonTextReader(reader));
                ParseObject(o);
            }
    
    		//	读取字符串后再解析
    		string s = File.ReadAllText(@"c:\person.json");
    		{
    			JObject o = (JObject)JToken.Parse(s);
    			ParseObject(o);
    		}
        }
    }
    

通过反射序列化json

  • 跟实体类转换

    public class Product
    {
    	//	"Name": "Apple"
    	public string Name;
    
    	//  "ExpiryDate": "2008-12-28T00:00:00"
    	public DateTime ExpiryDate;
    
    	//	"Price": 3.99
    	[DefaultValue(30)]		//	设置默认值,正常情况,对象默认值是 null,值类型默认值就是该类型默认初始值(不是该变量的初始值,比如 int 默认值是 0),配合 DefaultValueHandling.Ignore 可以减少 json 文件大小
    	public float Price;
    
    	//  "Sizes": ["Small", "Medium", "Large"]
    	public string[] Sizes;
    
    	//	"MyMemo":"meno"
    	[JsonProperty("MyMemo"){NullValueHandling = NullValueHandling.Ignore, IsReference = true}]	//	可以定制名称,为null时不序列化,多个变量指向同一对象时保持引用,等还有很多其它选项
    	public string Memo;
    
    	[JsonProperty]		//	让私有成员也能序列化
    	private string title;
    
    	[JsonIgnore]		//	不序列化
    	public string content;
    
    	//	"ExpiryDate":new Date(1230375600000)
    	[JsonConverter(typeof(JavaScriptDateTimeConverter))] 	//	修改序列化格式
    	public DateTime JSDate;
    
    	[JsonConverter(typeof(StringEnumConverter))]  //默认:枚举对应的整型数值 加上:枚举对应的字符,也就是现在可以用整数,也可以用字符串,输出时是字符串
    	public NullValueHandling NullValueHandling { get; set; }
    
    	[JsonExtensionData]		//	所有未识别的元素会加入这里,转成 json 时会原样输出
    	private IDictionary<string, JToken> _additionalData;
    
    	public string Domain { get; set; }
    	
    	public Product Owner;
    
    	//	可以增加一个函数来动态决定某个属性是否需要序列化,函数签名为 bool ShouldSerialize{属性或字段名}();
    	bool ShouldSerializeOwner()
    	{
    		return Owner != this;
    	}
    
    	public Product()
    	{
    		_additionalData = new Dictionary<string, JToken>();
    		Sizes = new string[]{"Default"};		//	反序列化时默认直接使用已有 Sizes 对象,会在后面追加元素,除非设置 ObjectCreationHandling.Replace,则会重新创建  Sizes 对象
    	}
    
    	[JsonConstructor]	//	反序列化时默认用无参构造函数,你可以指定用带参数构造函数,会用 Name 属性做为参数
    	public Product(string name)
    	{
    		Name = name;
    	}
    
    	//	跟实现 ISe
    	//	OnSerializing  	序列化前调用的函数
    	//	OnSerialized	序列化后调用的函数
    	//	OnDeserializing	反序列化前调用的函数
    	[OnDeserialized]		//	反序列化后调用的函数
    	private void OnDeserialized(StreamingContext context)
    	{
    		//	比如可以在反序列化后,从没有处理的属性 SAMAccountName 中解析出  Domain
    		string samAccountName = (string)_additionalData["SAMAccountName"];
    		Domain = samAccountName.Split('\\')[0];
    	}
    
    	[OnError]				//	错误处理
    	internal void OnError(StreamingContext context, ErrorContext errorContext)
    	{
    		errorContext.Handled = true;		//	处理完要设置成 true
    	}
    }
    
    public class Book : Product
    {}
    
    //	定制反序列化时生成派生类,CustomCreationConverter 是 JsonConverter 的派生类
    //	只能固定转成某个派生类,不能根据类型动态转成不同的派生类,需要动态转换参考 TypeNameHandling
    public class ProductConverter : CustomCreationConverter<Product>
    {
    	public override Product Create(Type objectType)
    	{
    		//	objectType 可以是 Product 或其派生类
    		return new Book();
    	}
    }
    
    //	定制序列化时只序列化某些属性
    public class DynamicContractResolver : DefaultContractResolver
    {
    	private readonly char _startingWithChar;
    
    	public DynamicContractResolver(char startingWithChar)
    	{
    		_startingWithChar = startingWithChar;
    	}
    
    	protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    	{
    		IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
    
    		// 只序列化以 _startingWithChar 字符开头的属性
    		properties = properties.Where(p => p.PropertyName.StartsWith(_startingWithChar.ToString())).ToList();
    
    		//	动态决定某个属性是否要序列化,跟上面的 ShouldSerializeOwner 是一样的效果,2者选其一就行
    		if (property.DeclaringType == typeof(Product) && property.PropertyName == "Owner")
    		{
    			property.ShouldSerialize = instance =>
    			{
    				Product e = (Product)instance;
    				return e.Owner != e;
    			};
    		}
    
    		return properties;
    	}
    }
    
    //	加了 OptIn ,则只有加 JsonProperty 的成员才会序列化
    //	默认是 OptOut,所有公有成员会被序列化(包括字段和属性)
    //	还有一个是 Fields,所有字段会被序列化(包括公共和私有)
    [JsonObject(MemberSerialization.OptIn)]
    public class Item
    {
    	
    	public	string Name;
    	//	没有加 JsonProperty,不会被序列化
    	public  int Age;
    }
    
    public class Example
    {
    	Product TEST_PRODUCT = new Product(){
    		Name = "Apple",
    		ExpiryDate = new DateTime(2008, 12, 28),
    		Price = 3.99M,
    		Sizes = new string[] { "Small", "Medium", "Large" }
    	};
    
    	//	写入文件
    	public static void WriteJsonFile()
    	{
    		//	直接写入文件
    		JsonSerializer serializer = CustomSerializer();
    		using (StreamWriter sw = new StreamWriter(@"c:\json.txt"))
    		using (JsonWriter writer = new JsonTextWriter(sw))
    		{
    			serializer.Serialize(writer, product);
    		}
    
    		//	转成字符串后写入文件
    		string json = JsonConvert.SerializeObject(TEST_PRODUCT, Formatting.Indented, CustomSettings());
    		File.WriteAllText("c:\\1.json", json);
    
    		//	输出 {"key1":"value1", "key2":"value2"}
    		string dics = JsonConvert.SerializeObject(new Dictionary<string, string>(){{"key1":"value1"},{"key2":"value2"}});
    	}
    
    	//	读取文件
    	public static void ReadJsonFile()
    	{
    		//	直接解析文件
    		JsonSerializer serializer = CustomSerializer();
    		using (StreamReader sr = new StreamReader(path, Encoding.Default))
    		{
    			Product book = serializer.Deserialize<Product>(sr);
    		}
    
    		//	读取字符串后再解析
    		string json = File.ReadAllText("c:\\1.json");
    		//	实际返回的是 Book 对象,如果有多种类型要替换,就加入多少 JsonConverter
    		Product book = JsonConvert.DeserializeObject<Product>(json, new ProductConverter(), new XXXConverter());
    	}
    
    	//	跟 JObject 转换
    	public static void ConvertWithObject()
    	{
    		//	实体类转 JObject
    		JObject o = JToken.FromObject(TEST_PRODUCT);
    
    		//	JObject 转 实体类
    		Person p = o.ToObject<Person>();
    	}
    
    	//	使用 json 更新实体
    	public static void UpdateObject()
    	{
    		string json = @"{
    			'Name': 'Apple',
    			'Price':1,
    			'Sizes':['Big']
    		}";
    
    		//	用 json 字符串中的值更新 TEST_PRODUCT 对象,对于数组会追加
    		//	结果:
    		//		Name = "Apple",
    		//		Price = 1,
    		//		Sizes = new string[] { "Small", "Medium", "Large", "Big" }
    		JsonConvert.PopulateObject(json, TEST_PRODUCT);
    
    	}
    	//	定制序列化选项
    	public static JsonSerializer CustomSerializer()
    	{
    		JsonSerializer serializer = new JsonSerializer();
    		serializer.Converters.Add(new JavaScriptDateTimeConverter());//	时间转成js时间格式,"ExpiryDate": "2008-12-28T00:00:00" 变成 "ExpiryDate":new Date(1230375600000)
    		serializer.NullValueHandling = NullValueHandling.Ignore;	//	null 值不做处理
    
    		//	还有很多可定制化选项,参考后面的 JsonSerializer 定制参数
    		//	也可以参考下一个函数 CustomSettings, 2者很多参数相机
    		return serializer;
    	}
    
    	//	定制序列化选项,JsonSerializerSettings 拥有跟 JsonSerializer 差不多的参数
    	//	只不过 JsonSerializerSettings 是给 JsonConvert.SerializeObject 用的
    	public static JsonSerializerSettings CustomSettings()
    	{
    		JsonSerializerSettings jsetting = new JsonSerializerSettings()
    		{
    			NullValueHandling = NullValueHandling.Ignore,			//	null 值不做处理
    			Error = delegate(object sender, ErrorEventArgs args)	//	错误处理程序,如果某个元素解析出错,该元素和所有祖先节点都会调用一次错误处理程序
    			{
    				errors.Add(args.ErrorContext.Error.Message);
    				args.ErrorContext.Handled = true;					//	不想被后面的处理,就要设置已捕获
    			},
    			Converters = {new JavaScriptDateTimeConverter()},		//	时间转成js时间格式,"ExpiryDate": "2008-12-28T00:00:00" 变成 "ExpiryDate":new Date(1230375600000)	
    			PreserveReferencesHandling = PreserveReferencesHandling.Objects,	//	如果有2个变量指向同一对象,则默认会序列化2份相同的数据,此时反序列化后会变成2个对象,有时候可能不满足需求,还可能造成循环引用
    																	//	设置该标志后,会在第一次序列化对象时加个属性"$id",比如 "Person" : {"$id": "1", "name":"n"},后续序列化该对象时变成 "Person" : {"$ref","1"}
    																	//	1是自动生成的对象编号,这样反序列化后就会指向同一对象
    																	//	在特性 [JsonObject], [JsonArray] 和 [JsonProperty].中加入 IsReference = true,可以定制这些对象默认开启保持引用
    			DefaultValueHandling = DefaultValueHandling.Ignore,		//	忽略默认值,可以减少json文件大小,这里的默认值不是字段初始时的值,而是类型初始值,比如 int 初始就是 0,可以用 [DefaultValue(默认值)] 特性修改某些属性的默认值不为该类型的默认值
    			ContractResolver = new DynamicContractResolver('A'),	//	只序列化以字符 A 开头的属性
    			TraceWriter = new MemoryTraceWriter(),					//	调试日志,你也可以自己实现 ITraceWriter,一般用默认的 MemoryTraceWriter 就够了
    			TypeNameHandling=TypeNameHandling.Auto,					//	是否自动存储类型信息,会自动增加 "$type" 属性,默认是None,对于派生类体系很有用,可设置为 Auto,当类型不符时自动存储类型信息
    			ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,	//	允许使用非公有默认构造函数
    			ObjectCreationHandling = ObjectCreationHandling.Replace, //	 反序列化时对已有对象怎么处理,Reuse(默认) 直接使用,Replace 替换,对于复杂对象很关键,比如 List 默认用 Reuse ,则在后面追加对象,改用 Replace 后变成先清空已有对象
    			MissingMemberHandling = MissingMemberHandling.Error,	//	反序列化时,缺少成员当做错误处理,默认是 Ignore 直接忽略
    			ReferenceLoopHandling = ReferenceLoopHandling.Ignore,	//	循环引用如何处理,默认 Ignore
    			MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead,	//	默认 $ 开头的内置属性(比如 $id $type)只能放在其它属性前面,设置成 ReadAhead 后也可以放在后面,但效率会降低,一般没必要
    		};
           
    		return jsetting
    	}
    
    	//	只反序列化部分对象
    	public static void DeserializePartialJson()
    	{
    		//	对象这样转换
    		JObject jo = JObject.Parse(@"'data':{'name':'qml'}");
    		JToken jdata = jo["data"];
    		MyData = jdata.ToObject<MyData>();
    		//	数组这样转换
    		JObject jo = JObject.Parse(@"'data':['1','2']");
    		IList<JToken> jresults = jo["data"].Children().ToList();
    		IList<string> results = new List<SearchResult>();
    		foreach (JToken jresult in jresults)
    		{
    			string result = jresult.ToObject<string>();
    			results.Add(result);
    		}
    	}
    }
    
  • JsonSerializer 定制参数
    使用 JsonSerializer 可以全面精细的控制序列化流程,下面是可以定制的属性

    • DateFormatHandling
      定制日期序列化
    • MissingMemberHandling
      定制字段缺失处理
    • ReferenceLoopHandling
      循环引用处理,比如自己的成员引用自己
    • NullValueHandling
      空值处理
    • DefaultValueHandling
      默认值处理
    • ObjectCreationHandling
      创建对象
    • TypeNameHandling
      是否序列化类型名称
    • TypeNameAssemblyFormat
      使用简单类型名还是完整类型名
    • SerializationBinder
      自定义类型到类型名称的转换
    • MetadataPropertyHandling
      决定metadata 属性的读取顺序,$type $id 这种字段叫做 metadata 属性
    • ConstructorHandling
      决定创建对象时如何构造对象
    • Converters
      自定义转换过滤器
    • ContractResolver
      自定义序列化某种类型对象
    • TraceWriter
      关联日志调试器
    • Error
      错误捕获器

嵌套派生类体系的序列化

		//	派生类体系,下面示例共用
		class Address
		{
			public string address;
			public int number;

			public virtual string Print()
			{
				return $"{address},{number}";
			}
		}

		class Address1 : Address
		{
			public int order;
			public float price;

			public override string Print()
			{
				return $"{address},{number},{order},{price}";
			}
		}

		class Address2 : Address
		{
			public string title;
			public string content;

			public override string Print()
			{
				return $"{address},{number},{title},{content}";
			}
		}
  1. 把派生类序列化成json字符串,存储在某个字段
    逻辑简单,但json序列化的字符串不好阅读,可用方法2替代

    	class Person
    	{
    		public int age;
    		public string data;		//	派生类序列化的json
    		public int dataType;	//	派生类类型
    	}
    
    	class Main
    	{
    		public static void Main()
    		{
    			Address1 a = new Address1() { address = "a/b/c", number = 112, order=110, price=1.112f };
    			Person b = new Person() { age = 10, data = JObject.Parse(JsonConvert.SerializeObject(a)),  dataType=1};
    			using (StreamWriter sw = File.CreateText("d:\\1.json"))
    			{
    				//	序列化成字符串
    				string json = JsonConvert.SerializeObject(b, Formatting.Indented);
    				sw.Write(json);
    			}
    
    			using (StreamReader sr = new StreamReader("d:\\1.json", Encoding.Default))
    			{
    				//	反序列化
    				Person newb = JsonConvert.DeserializeObject<Person>(sr.ReadToEnd());
    
    				if ( newb.dataType == 1)
    				{
    					Address1 newa = JsonConvert.DeserializeObject<Address1>(newb.data);
    					Console.WriteLine($"newa={newa.address},{newa.number},{newa.order},{newa.price} newb={newb.age},{newb.data}");
    				}
    			}
    		}
    	}
    
  2. 把派生类序列化成 JToken,存储在某个字段
    (推荐)逻辑简单,且方便阅读

    	class Person 
    	{
    		public int age;
    		public JToken? data;	//	派生类序列化的 JToken
    		public int dataType;	//	派生类类型
    		[JsonIgnore]
    		public Address address;
    		[OnDeserialized]
    		void OnDeserialized()
    		{
    			//	动态转成派生类
    			if ( dataType == 1 && data != null )
    				address = data.ToObject<Address1>();
    		}
    	}
    
    	class Main
    	{
    		public static void Main()
    		{
    			Address1 a = new Address1() { address = "a/b/c", number = 112, order=110, price=1.112f };
    			Person b = new Person() { age = 10, data = JObject.FromObject(a),  dataType=1};
    			using (StreamWriter sw = File.CreateText("d:\\1.json"))
    			{
    				//	序列化成字符串
    				string json = JsonConvert.SerializeObject(b, Formatting.Indented);
    				sw.Write(json);
    			}
    
    			using (StreamReader sr = new StreamReader("d:\\1.json", Encoding.Default))
    			{
    				//	反序列化
    				Person newb = JsonConvert.DeserializeObject<Person>(sr.ReadToEnd());
    				Address1 newa = newb.address;
    				Console.WriteLine($"newa={newa.address},{newa.number},{newa.order},{newa.price} newb={newb.age},{newb.data}");
    			}
    		}
    	}
    
  3. 利用newtonsoft.json自带的存储对象类型功能
    (推荐)全自动处理,缺点是如果重构修改了类型名称,会导致已有json解析不了,而且不适合跟服务端通信,适用场景是保存运行时动态配置

    	public class Person
    	{
    		public int age;
    		public Address data;
    	}
    
    	class Main
    	{
    		public static void Main()
    		{
    			Address1 a = new Address1() { address = "a/b/c", number = 112, order=110, price=1.112f };
    			Person b = new Person() { age = 10, data = a};
    			using (StreamWriter sw = File.CreateText("d:\\1.json"))
    			{
    				//	设置 TypeNameHandling.Auto 后,如果字段类型和对象实际类型不一致,则会增加 "$type"="类名,程序集名" 属性记录对象实际类型 
    				string json = JsonConvert.SerializeObject(b, Formatting.Indented, new JsonSerializerSettings() { TypeNameHandling=TypeNameHandling.Auto});
    				sw.Write(json);
    			}
    
    			using (StreamReader sr = new StreamReader("d:\\1.json", Encoding.Default))
    			{
    				//	反序列化
    				Person newb = JsonConvert.DeserializeObject<Person>(sr.ReadToEnd(), new AddressConverter());
    
    				Address1 newa = (Address1)newb.data;
    				Console.WriteLine($"newa={newa.address},{newa.number},{newa.order},{newa.price} newb={newb.age},{newb.data}");
    			}
    		}
    	}
    
  4. 利用JsonConverter定制转换器
    (推荐)

     	//	自定义转换器
     	public class AddressConverter : JsonConverter
     	{
     		bool m_canRead = true;
     		bool m_canWrite = true;
    
     		public override bool CanRead => m_canRead;
     		public override bool CanWrite => m_canWrite;
    
     		public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
     		{
     			//	调用下面这2个函数均会造成死循环,会嵌套调用 WriteJson, writer 参数不一样,其它参数一样
     			//	serializer.Serialize(writer, value);
     			//	JObject address = JObject.FromObject(value,serializer);		//	不传 serializer 就不会死循环,但序列化配置也同时恢复成默认
    
     			//	直接调用 FromObject 会造成死循环,重载 CanWrite 在调用前先禁用该转换器,这样就不会死循环
     			m_canWrite = false;		
     			JObject address = (JObject)JObject.FromObject(value,serializer);
     			m_canWrite = true;
    
     			if ( value is Address1 )
     			{
     				address["AddressType"] = 1;
     			}
     			else
     			{
     				address["AddressType"] = 2;
     			}
    
     			address.WriteTo(writer, serializer.Converters?.ToArray());
    
     			//	常用的还有以下方法进行处理
     			//	1. 直接拼接
     			//		var vec = (Vector2)value;
     			//		writer.WriteStartObject();				//	对象开始
     			//		writer.WritePropertyName("x");			//	属性和值之间不需要其它东东
     			//		writer.WriteValue(vec.x);
     			//		writer.WritePropertyName("y");
     			//		writer.WriteValue(vec.y);
     			//		writer.WriteEndObject();				//	对象结束
     		}
    
     		public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
     		{
     			//	调用下面这2个函数均会造成死循环,会嵌套调用 ReadJson, objectType 参数不一样,其它参数一样
     			//	serializer.Deserialize(reader, objectType);
     			//	JObject jsonObject = JObject.Load(reader);
     			//	JObject address = jsonObject.ToObject(objectType,serializer);	//	不传 serializer 就不会死循环,但序列化配置也同时恢复成默认
    
     			try
     			{
     				var jsonObject = JObject.Load(reader);
     				Type t = null;
     				if (jsonObject.TryGetValue("AddressType", out JToken addressType))
     				{
     					switch ((int)addressType)
     					{
     						case 1: //	Address1
     							t = typeof(Address1);
     							break;
     						case 2: //	Address2
     							t = typeof(Address2);
     							break;
     					}
     				}
    
     				//	直接调用 FromObject 会造成死循环,重载 CanRead 在调用前先禁用该转换器,这样就不会死循环
     				m_canRead = false;
     				object target = jsonObject.ToObject(t, serializer);
     				m_canRead = true;
    
     				//	常见的还有以下方法进行处理
     				//	1. 使用 Populate
     				//		var target = new Address1();
     				//		serializer.Populate(jsonObject.CreateReader(), target);
     				//	2. 直接拼接
     				//		//	没 Read 前,TokenType=StartObject,也就是 {
     				//		reader.Read();			//	第1次Read,TokenType=PropName , Value="x"
     				//		reader.Read();			//	第2次Read,TokenType=Float
     				//		float x = reader.Value;
     				//		reader.Read();			//	第3次Read,TokenType=PropName , Value="y"
     				//		reader.Read();			//	第4次Read,TokenType=Float
     				//		float y = reader.Value;
     				//		reader.Read();			//	第5次Read,TokenType=EndObject,也就是 },
     				//		也就是说对象不用读取{,但要读取},这主要是跟普通值处理流程一致,普通值外部已经调用完 Read,在 ReadJson 函数中直接使用 reader.Value
     				return target;
     			}
     			catch (Exception ex)
     			{
     				throw new Exception("解析异常:" + ex.Message);
     			}
     		}
    
     		public override bool CanConvert(Type objectType)
     		{
     			return typeof(Address).IsAssignableFrom(objectType);
     		}
     	}
    
  5. 总结
    自己写自己读的场景中优先使用方案3
    跟别人通信的情况下(跟服务端通信或读取策划配置表),方案2和方案4都可以采用,方案2更简单点,如果类型字段记录在外部,则只能用方案2
    方案1不推荐,可用方案2替代

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值