From 23c9ade79ec8566818c5eceebf0c63fbb1f28932 Mon Sep 17 00:00:00 2001 From: milimoe <110188673+milimoe@users.noreply.github.com> Date: Sat, 3 Jun 2023 18:57:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=A5=E6=8A=B1System.Text.Json=20(#27)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 添加适用于DataSet和DateTime的JSON自定义转换器。 * 添加JsonManager,删除JsonObject并仅使用SocketObject * 移除Newtonsoft.Json引用 --- Api/Utility/General.cs | 23 +- FunGame.Core.csproj | 4 - .../Common/JsonConverter/DataSetConverter.cs | 302 ++++++++++++++++++ .../Common/JsonConverter/DateTimeConverter.cs | 31 ++ Library/Common/Network/ClientSocket.cs | 2 +- Library/Common/Network/JsonObject.cs | 67 ---- Library/Common/Network/Socket.cs | 2 +- Library/Common/Network/SocketObject.cs | 49 ++- Library/Constant/General.cs | 5 +- Service/JsonManager.cs | 88 +++++ Service/SocketManager.cs | 36 +-- 11 files changed, 492 insertions(+), 117 deletions(-) create mode 100644 Library/Common/JsonConverter/DataSetConverter.cs create mode 100644 Library/Common/JsonConverter/DateTimeConverter.cs delete mode 100644 Library/Common/Network/JsonObject.cs create mode 100644 Service/JsonManager.cs diff --git a/Api/Utility/General.cs b/Api/Utility/General.cs index 8f44926..7b7851b 100644 --- a/Api/Utility/General.cs +++ b/Api/Utility/General.cs @@ -1,6 +1,5 @@ using System.Net.NetworkInformation; using System.Security.Cryptography; -using System.Text.Json; using System.Text.RegularExpressions; using Milimoe.FunGame.Core.Library.Constant; @@ -115,6 +114,28 @@ namespace Milimoe.FunGame.Core.Api.Utility } return -1; } + + /// + /// 返回目标对象的Json字符串 + /// + /// + /// + /// + public static string JsonSerialize(T obj) + { + return Service.JsonManager.GetString(obj); + } + + /// + /// 反序列化Json对象 + /// + /// + /// + /// + public static T? JsonDeserialize(string json) + { + return Service.JsonManager.GetObject(json); + } } #endregion diff --git a/FunGame.Core.csproj b/FunGame.Core.csproj index fbfc64c..fd949d3 100644 --- a/FunGame.Core.csproj +++ b/FunGame.Core.csproj @@ -24,8 +24,4 @@ embedded - - - - diff --git a/Library/Common/JsonConverter/DataSetConverter.cs b/Library/Common/JsonConverter/DataSetConverter.cs new file mode 100644 index 0000000..1716742 --- /dev/null +++ b/Library/Common/JsonConverter/DataSetConverter.cs @@ -0,0 +1,302 @@ +using System.Data; +using System.Text.Json; +using System.Text.Json.Serialization; +using Milimoe.FunGame.Core.Library.Constant; +using Milimoe.FunGame.Core.Service; + +namespace Milimoe.FunGame.Core.Library.Common.JsonConverter +{ + public class DataSetConverter : JsonConverter + { + public override DataSet Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options) + { + DataSet ds = new(); + DataTable dt = new(); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.PropertyName) + { + string property = reader.GetString() ?? ""; + + switch (property) + { + case "TableName": + reader.Read(); + string tableName = reader.GetString() ?? ""; + dt = new DataTable(tableName); + ds.Tables.Add(dt); + break; + + case "Columns": + reader.Read(); + ReadColumns(reader, dt); + break; + + case "Rows": + reader.Read(); + ReadRows(reader, dt); + break; + } + } + } + + return ds; + } + + public override void Write(Utf8JsonWriter writer, DataSet value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + + writer.WriteString("TableName", value.Tables[0].TableName); + + writer.WritePropertyName("Columns"); + writer.WriteStartArray(); + + foreach (DataColumn column in value.Tables[0].Columns) + { + writer.WriteStartObject(); + + writer.WriteString("ColumnName", column.ColumnName); + writer.WriteString("DataType", column.DataType.FullName); + + writer.WriteEndObject(); + } + + writer.WriteEndArray(); + + writer.WritePropertyName("Rows"); + writer.WriteStartArray(); + + foreach (DataRow row in value.Tables[0].Rows) + { + writer.WriteStartArray(); + + for (int i = 0; i < value.Tables[0].Columns.Count; i++) + { + object? rowValue = row[i]; + + switch (value.Tables[0].Columns[i].DataType.FullName) + { + case "System.Boolean": + writer.WriteBooleanValue((bool)rowValue); + break; + + case "System.Byte": + writer.WriteNumberValue((byte)rowValue); + break; + + case "System.Char": + writer.WriteStringValue(value.ToString()); + break; + + case "System.DateTime": + writer.WriteStringValue(((DateTime)rowValue).ToString(General.GeneralDateTimeFormat)); + break; + + case "System.Decimal": + writer.WriteNumberValue((decimal)rowValue); + break; + + case "System.Double": + writer.WriteNumberValue((double)rowValue); + break; + + case "System.Guid": + writer.WriteStringValue(value.ToString()); + break; + + case "System.Int16": + writer.WriteNumberValue((short)rowValue); + break; + + case "System.Int32": + writer.WriteNumberValue((int)rowValue); + break; + + case "System.Int64": + writer.WriteNumberValue((long)rowValue); + break; + + case "System.SByte": + writer.WriteNumberValue((sbyte)rowValue); + break; + + case "System.Single": + writer.WriteNumberValue((float)rowValue); + break; + + case "System.String": + writer.WriteStringValue((string)rowValue); + break; + + case "System.UInt16": + writer.WriteNumberValue((ushort)rowValue); + break; + + case "System.UInt32": + writer.WriteNumberValue((uint)rowValue); + break; + + case "System.UInt64": + writer.WriteNumberValue((ulong)rowValue); + break; + } + } + + writer.WriteEndArray(); + } + + writer.WriteEndArray(); + + writer.WriteEndObject(); + } + + #pragma warning disable CA1822 // 不需要设为静态 + private void ReadColumns(Utf8JsonReader reader, DataTable dataTable) + #pragma warning restore CA1822 // 不需要设为静态 + { + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) + { + break; + } + + if (reader.TokenType == JsonTokenType.StartObject) + { + DataColumn column = new(); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.PropertyName) + { + string propertyName = reader.GetString() ?? ""; + + switch (propertyName) + { + case "ColumnName": + reader.Read(); + column.ColumnName = reader.GetString(); + break; + + case "DataType": + reader.Read(); + Type dataType = Type.GetType(reader.GetString() ?? "") ?? typeof(object); + column.DataType = dataType; + break; + } + } + + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + } + + dataTable.Columns.Add(column); + } + } + } + + private void ReadRows(Utf8JsonReader reader, DataTable dataTable) + { + object[] values = new object[dataTable.Columns.Count]; + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) + { + break; + } + + if (reader.TokenType == JsonTokenType.StartArray) + { + int index = 0; + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) + { + break; + } + + switch (dataTable.Columns[index].DataType.ToString()) + { + case "System.Boolean": + values[index] = reader.GetBoolean(); + break; + + case "System.Byte": + values[index] = reader.GetByte(); + break; + + case "System.Char": + values[index] = (reader.GetString() ?? "")[0]; + break; + + case "System.DateTime": + string dateString = reader.GetString() ?? ""; + if (DateTime.TryParseExact(dateString, General.GeneralDateTimeFormat, null, System.Globalization.DateTimeStyles.None, out DateTime result)) + { + values[index] = result; + } + values[index] = DateTime.MinValue; + break; + + case "System.Decimal": + values[index] = reader.GetDecimal(); + break; + + case "System.Double": + values[index] = reader.GetDouble(); + break; + + case "System.Guid": + values[index] = Guid.Parse(reader.GetString() ?? Guid.Empty.ToString()); + break; + + case "System.Int16": + values[index] = reader.GetInt16(); + break; + + case "System.Int32": + values[index] = reader.GetInt32(); + break; + + case "System.Int64": + values[index] = reader.GetInt64(); + break; + + case "System.SByte": + values[index] = reader.GetSByte(); + break; + + case "System.Single": + values[index] = reader.GetSingle(); + break; + + case "System.String": + values[index] = reader.GetString() ?? ""; + break; + + case "System.UInt16": + values[index] = reader.GetUInt16(); + break; + + case "System.UInt32": + values[index] = reader.GetUInt32(); + break; + + case "System.UInt64": + values[index] = reader.GetUInt64(); + break; + } + index++; + } + dataTable.Rows.Add(values); + } + } + } + } +} diff --git a/Library/Common/JsonConverter/DateTimeConverter.cs b/Library/Common/JsonConverter/DateTimeConverter.cs new file mode 100644 index 0000000..db25b90 --- /dev/null +++ b/Library/Common/JsonConverter/DateTimeConverter.cs @@ -0,0 +1,31 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.Core.Library.Common.JsonConverter +{ + public class DateTimeConverter : JsonConverter + { + public override DateTime Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.String) + { + throw new JsonException(); + } + + string date = reader.GetString() ?? ""; + + if (DateTime.TryParseExact(date, General.GeneralDateTimeFormat, null, System.Globalization.DateTimeStyles.None, out DateTime result)) + { + return result; + } + + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString(General.GeneralDateTimeFormat)); + } + } +} diff --git a/Library/Common/Network/ClientSocket.cs b/Library/Common/Network/ClientSocket.cs index 8143681..22bf36d 100644 --- a/Library/Common/Network/ClientSocket.cs +++ b/Library/Common/Network/ClientSocket.cs @@ -66,7 +66,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network { if (Instance != null) { - if (SocketManager.Send(Instance, type, Token, objs) == SocketResult.Success) + if (SocketManager.Send(Instance, new(type, Token, objs)) == SocketResult.Success) { return SocketResult.Success; } diff --git a/Library/Common/Network/JsonObject.cs b/Library/Common/Network/JsonObject.cs deleted file mode 100644 index 9ac096b..0000000 --- a/Library/Common/Network/JsonObject.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Milimoe.FunGame.Core.Library.Constant; - -namespace Milimoe.FunGame.Core.Library.Common.Network -{ - [Serializable] - public struct JsonObject - { - public SocketMessageType MessageType { get; } = SocketMessageType.Unknown; - public Guid Token { get; } - public object[] Parameters { get; } - public string JsonString { get; } - - private JArray? JArray; - - public JsonObject(SocketMessageType MessageType, Guid Token, params object[] Parameters) - { - this.MessageType = MessageType; - this.Token = Token; - this.Parameters = Parameters; - this.JsonString = JsonConvert.SerializeObject(this, Formatting.Indented); - } - - public T? GetObject(int i) - { - if (i >= Parameters.Length) throw new IndexOutOfArrayLengthException(); - JArray ??= JArray.FromObject(Parameters); - return JArray[i].ToObject(); - } - - public static string GetString(SocketMessageType MessageType, Guid Token, params object[] Parameters) - { - return new JsonObject(MessageType, Token, Parameters).JsonString; - } - - public static JsonObject GetObject(string JsonString) - { - return JsonConvert.DeserializeObject(JsonString); - } - - public static JsonObject[] GetObjects(string JsonString) - { - List jsons = new(); - - JsonSerializer serializer = new(); - JsonTextReader reader = new(new StringReader(JsonString)) - { - SupportMultipleContent = true - }; - - while (true) - { - if (!reader.Read()) - { - break; - } - - JsonObject json = serializer.Deserialize(reader); - - jsons.Add(json); - } - - return jsons.ToArray(); - } - } -} diff --git a/Library/Common/Network/Socket.cs b/Library/Common/Network/Socket.cs index 45765e6..aa6ff59 100644 --- a/Library/Common/Network/Socket.cs +++ b/Library/Common/Network/Socket.cs @@ -45,7 +45,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network { if (Instance != null) { - if (SocketManager.Send(type, Token, objs) == SocketResult.Success) + if (SocketManager.Send(new(type, Token, objs)) == SocketResult.Success) { return SocketResult.Success; } diff --git a/Library/Common/Network/SocketObject.cs b/Library/Common/Network/SocketObject.cs index ddc6a11..bcb5fe9 100644 --- a/Library/Common/Network/SocketObject.cs +++ b/Library/Common/Network/SocketObject.cs @@ -1,4 +1,6 @@ -using Milimoe.FunGame.Core.Library.Constant; +using System.Text.Json.Serialization; +using Milimoe.FunGame.Core.Library.Constant; +using Milimoe.FunGame.Core.Service; namespace Milimoe.FunGame.Core.Library.Common.Network { @@ -8,34 +10,47 @@ namespace Milimoe.FunGame.Core.Library.Common.Network public SocketMessageType SocketType { get; } = SocketMessageType.Unknown; public Guid Token { get; } = Guid.Empty; public object[] Parameters { get; } = Array.Empty(); - public int Length { get; } = 0; - private JsonObject Json { get; } + public int Length => Parameters.Length; - public SocketObject(JsonObject json) + /// + /// 从参数列表中获取指定索引的参数的Json字符串 + /// -- 此方法仅返回Json字符串,对象类型请使用反序列化方法GetParam() -- + /// -- 当然也可以自己反序列化 -- + /// + /// 索引 + /// + /// 索引超过数组上限 + public object? this[int index] { - Json = json; - SocketType = Json.MessageType; - Token = Json.Token; - Parameters = Json.Parameters; - Length = Parameters.Length; + get + { + if (index >= Parameters.Length) throw new IndexOutOfArrayLengthException(); + object? obj = Parameters[index]; + return JsonManager.GetObject(obj.ToString() ?? ""); + } } - public SocketObject() + [JsonConstructor] + public SocketObject(SocketMessageType SocketType, Guid Token, params object[] Parameters) { - Json = new JsonObject(SocketMessageType.Unknown, Guid.Empty, Array.Empty()); - SocketType = Json.MessageType; - Token = Json.Token; - Parameters = Json.Parameters; - Length = Parameters.Length; + this.SocketType = SocketType; + this.Token = Token; + if (Parameters != null && Parameters.Length > 0) this.Parameters = Parameters; } /// - /// 从参数列表中获取指定类型的参数 + /// 从参数列表中获取指定类型和索引的参数 /// /// 类型 /// 索引 /// 类型的参数 /// 索引超过数组上限 - public T? GetParam(int index) => Json.GetObject(index); + public T? GetParam(int index) + { + if (index >= Parameters.Length) throw new IndexOutOfArrayLengthException(); + object obj = Parameters[index]; + T? result = JsonManager.GetObject(obj.ToString() ?? ""); + return result; + } } } diff --git a/Library/Constant/General.cs b/Library/Constant/General.cs index 01e721e..d4d4794 100644 --- a/Library/Constant/General.cs +++ b/Library/Constant/General.cs @@ -8,8 +8,9 @@ namespace Milimoe.FunGame.Core.Library.Constant // Static Variable public static Empty EntityInstance { get; } = new(); public static User UnknownUserInstance { get; } = new(); - public static Room HallInstance { get; } = Api.Utility.Factory.GetHall(); - public static Encoding DefaultEncoding { get; } = Encoding.Unicode; + public static Room HallInstance => Api.Utility.Factory.GetHall(); + public static Encoding DefaultEncoding => Encoding.Unicode; + public static string GeneralDateTimeFormat => "yyyy-MM-dd HH:mm:ss.fff"; // Const public const int MaxRetryTimes = 20; diff --git a/Service/JsonManager.cs b/Service/JsonManager.cs new file mode 100644 index 0000000..619dfb9 --- /dev/null +++ b/Service/JsonManager.cs @@ -0,0 +1,88 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Milimoe.FunGame.Core.Library.Common.JsonConverter; + +namespace Milimoe.FunGame.Core.Service +{ + public class JsonManager + { + private static bool _IsFirst = true; + private readonly static JsonSerializerOptions _GeneralOptions = new() + { + WriteIndented = true, + ReferenceHandler = ReferenceHandler.IgnoreCycles + }; + + /// + /// 默认的JsonSerializerOptions + /// + internal static JsonSerializerOptions GeneralOptions + { + get + { + if (_IsFirst) + { + // 首次使用时,向其添加自定义转换器 + _IsFirst = false; + AddConverter(new DataSetConverter()); + AddConverter(new DateTimeConverter()); + } + return _GeneralOptions; + } + } + + /// + /// 注册一个自定义转换器 + /// + /// + internal static void AddConverter(JsonConverter converter) + { + _GeneralOptions.Converters.Add(converter); + } + + /// + /// 获取Json字符串 + /// + /// + /// + /// + public static string GetString(T obj) + { + return JsonSerializer.Serialize(obj, GeneralOptions); + } + + /// + /// 反序列化Json对象 + /// + /// + /// + /// + public static T? GetObject(string json) + { + return JsonSerializer.Deserialize(json, GeneralOptions); + } + + /// + /// 反序列化Json对象,此方法可能无法返回正确的类型,请注意辨别 + /// + /// + /// + public static object? GetObject(string json) + { + return JsonSerializer.Deserialize(json, GeneralOptions); + } + + /// + /// 反序列化多个Json对象 + /// 注意必须是相同的Json对象才可以使用此方法解析 + /// + /// + /// + /// + public static List GetObjects(string json) + { + json = "[" + json.Replace("}{", "},{") + "]"; // 将Json字符串转换为数组 + return JsonSerializer.Deserialize>(json, GeneralOptions) ?? new List(); + } + } +} diff --git a/Service/SocketManager.cs b/Service/SocketManager.cs index 6171b8a..4f66e92 100644 --- a/Service/SocketManager.cs +++ b/Service/SocketManager.cs @@ -116,14 +116,13 @@ namespace Milimoe.FunGame.Core.Service /// 用于服务器端向客户端Socket发送信息 /// /// 客户端Socket - /// 通信类型 - /// 参数 + /// Socket信息容器 /// 通信结果 - internal static SocketResult Send(Socket ClientSocket, SocketMessageType type, Guid token, params object[] objs) + internal static SocketResult Send(Socket ClientSocket, Library.Common.Network.SocketObject SocketObject) { - if (ClientSocket != null && objs != null && objs.Length > 0) + if (ClientSocket != null) { - if (ClientSocket.Send(General.DefaultEncoding.GetBytes(Library.Common.Network.JsonObject.GetString(type, token, objs))) > 0) + if (ClientSocket.Send(General.DefaultEncoding.GetBytes(JsonManager.GetString(SocketObject))) > 0) { return SocketResult.Success; } @@ -135,18 +134,13 @@ namespace Milimoe.FunGame.Core.Service /// /// 用于客户端向服务器Socket发送信息 /// - /// 通信类型 - /// 参数 + /// Socket信息容器 /// 通信结果 - internal static SocketResult Send(SocketMessageType type, Guid token, params object[] objs) + internal static SocketResult Send(Library.Common.Network.SocketObject SocketObject) { - if (objs is null || objs.Length <= 0) - { - objs = new object[] { "" }; - } if (Socket != null) { - if (Socket.Send(General.DefaultEncoding.GetBytes(Library.Common.Network.JsonObject.GetString(type, token, objs))) > 0) + if (Socket.Send(General.DefaultEncoding.GetBytes(JsonManager.GetString(SocketObject))) > 0) { return SocketResult.Success; } @@ -170,8 +164,7 @@ namespace Milimoe.FunGame.Core.Service if (length > 0) { string msg = General.DefaultEncoding.GetString(buffer, 0, length); - Library.Common.Network.JsonObject json = Library.Common.Network.JsonObject.GetObject(msg); - result = new Library.Common.Network.SocketObject(json); + result = JsonManager.GetObject(msg); // 客户端接收消息,广播ScoketObject到每个UIModel OnSocketReceive(result); return result; @@ -196,8 +189,7 @@ namespace Milimoe.FunGame.Core.Service if (length > 0) { string msg = General.DefaultEncoding.GetString(buffer, 0, length); - Library.Common.Network.JsonObject json = Library.Common.Network.JsonObject.GetObject(msg); - result = new Library.Common.Network.SocketObject(json); + result = JsonManager.GetObject(msg); return result; } } @@ -219,11 +211,9 @@ namespace Milimoe.FunGame.Core.Service if (length > 0) { string msg = General.DefaultEncoding.GetString(buffer, 0, length); - Library.Common.Network.JsonObject[] jsons = Library.Common.Network.JsonObject.GetObjects(msg); - foreach (Library.Common.Network.JsonObject json in jsons) + foreach (Library.Common.Network.SocketObject obj in JsonManager.GetObjects(msg)) { // 客户端接收消息,广播ScoketObject到每个UIModel - Library.Common.Network.SocketObject obj = new(json); result.Add(obj); OnSocketReceive(obj); } @@ -248,11 +238,9 @@ namespace Milimoe.FunGame.Core.Service if (length > 0) { string msg = General.DefaultEncoding.GetString(buffer, 0, length); - Library.Common.Network.JsonObject[] jsons = Library.Common.Network.JsonObject.GetObjects(msg); - foreach (Library.Common.Network.JsonObject json in jsons) + foreach (Library.Common.Network.SocketObject obj in JsonManager.GetObjects(msg)) { - Library.Common.Network.SocketObject so = new(json); - result.Add(so); + result.Add(obj); } } }