diff --git a/Netch/Controllers/UpdateChecker.cs b/Netch/Controllers/UpdateChecker.cs index 4a48994c..930e213f 100644 --- a/Netch/Controllers/UpdateChecker.cs +++ b/Netch/Controllers/UpdateChecker.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; using System.IO; using System.Net; +using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; using Netch.Models.GitHubRelease; using Netch.Utils; -using Newtonsoft.Json; using static Netch.Updater.Updater; namespace Netch.Controllers @@ -43,7 +43,7 @@ namespace Netch.Controllers var json = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(url)); - var releases = JsonConvert.DeserializeObject>(json); + var releases = JsonSerializer.Deserialize>(json); LatestRelease = VersionUtil.GetLatestRelease(releases, isPreRelease); LatestVersionNumber = LatestRelease.tag_name; LatestVersionUrl = LatestRelease.html_url; diff --git a/Netch/Forms/MainForm.cs b/Netch/Forms/MainForm.cs index d726cda3..c353671d 100644 --- a/Netch/Forms/MainForm.cs +++ b/Netch/Forms/MainForm.cs @@ -729,14 +729,16 @@ namespace Netch.Forms private void EditServerPictureBox_Click(object sender, EventArgs e) { // 当前ServerComboBox中至少有一项 - if (ServerComboBox.SelectedIndex == -1) + if (!(ServerComboBox.SelectedItem is Server server)) { MessageBoxX.Show(i18N.Translate("Please select a server first")); return; } + if (!server.Valid()) + return; + Hide(); - var server = Global.Settings.Server[ServerComboBox.SelectedIndex]; ServerHelper.GetUtilByTypeName(server.Type).Edit(server); LoadServers(); Configuration.Save(); @@ -774,16 +776,18 @@ namespace Netch.Forms private void CopyLinkPictureBox_Click(object sender, EventArgs e) { // 当前ServerComboBox中至少有一项 - if (ServerComboBox.SelectedIndex == -1) + if (!(ServerComboBox.SelectedItem is Server server)) { MessageBoxX.Show(i18N.Translate("Please select a server first")); return; } + if (!server.Valid()) + return; + try { //听说巨硬BUG经常会炸,所以Catch一下 :D - var server = (Server) ServerComboBox.SelectedItem; string text; if (ModifierKeys == Keys.Control) text = ShareLink.GetNetchLink(server); diff --git a/Netch/Global.cs b/Netch/Global.cs index 172e25a8..6cee91ed 100644 --- a/Netch/Global.cs +++ b/Netch/Global.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; +using System.Text.Encodings.Web; +using System.Text.Json; using System.Threading; using System.Windows.Forms; using WindowsJobAPI; @@ -114,5 +116,12 @@ namespace Netch /// 主窗体的静态实例 /// public static MainForm MainForm => _mainForm ??= new MainForm(); + + public static JsonSerializerOptions NewDefaultJsonSerializerOptions => new() + { + WriteIndented = true, + IgnoreNullValues = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; } } \ No newline at end of file diff --git a/Netch/Models/IServerUtil.cs b/Netch/Models/IServerUtil.cs index 32c36a6e..feb05b90 100644 --- a/Netch/Models/IServerUtil.cs +++ b/Netch/Models/IServerUtil.cs @@ -1,6 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Netch.Controllers; -using Newtonsoft.Json.Linq; namespace Netch.Models { @@ -28,7 +28,7 @@ namespace Netch.Models /// string[] UriScheme { get; } - Server ParseJObject(in JObject j); + public abstract Type ServerType { get; } public void Edit(Server s); diff --git a/Netch/Netch.csproj b/Netch/Netch.csproj index 784c0b96..1cef7551 100644 --- a/Netch/Netch.csproj +++ b/Netch/Netch.csproj @@ -43,9 +43,9 @@ - + diff --git a/Netch/Servers/Shadowsocks/SSUtil.cs b/Netch/Servers/Shadowsocks/SSUtil.cs index 88878238..5526e73c 100644 --- a/Netch/Servers/Shadowsocks/SSUtil.cs +++ b/Netch/Servers/Shadowsocks/SSUtil.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; using System.Text.RegularExpressions; using System.Web; using Netch.Controllers; @@ -8,8 +9,6 @@ using Netch.Models; using Netch.Servers.Shadowsocks.Form; using Netch.Servers.Shadowsocks.Models.SSD; using Netch.Utils; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace Netch.Servers.Shadowsocks { @@ -25,10 +24,7 @@ namespace Netch.Servers.Shadowsocks public string[] UriScheme { get; } = {"ss", "ssd"}; - public Server ParseJObject(in JObject j) - { - return j.ToObject(); - } + public Type ServerType { get; } = typeof(Shadowsocks); public void Edit(Server s) { @@ -80,7 +76,7 @@ namespace Netch.Servers.Shadowsocks public IEnumerable ParseSsdUri(string s) { - var json = JsonConvert.DeserializeObject
(ShareLink.URLSafeBase64Decode(s.Substring(6))); + var json = JsonSerializer.Deserialize
(ShareLink.URLSafeBase64Decode(s.Substring(6))); return json.servers.Select(server => new Shadowsocks { diff --git a/Netch/Servers/ShadowsocksR/SSRUtil.cs b/Netch/Servers/ShadowsocksR/SSRUtil.cs index 6bd40fcb..dc1e83a5 100644 --- a/Netch/Servers/ShadowsocksR/SSRUtil.cs +++ b/Netch/Servers/ShadowsocksR/SSRUtil.cs @@ -6,7 +6,6 @@ using Netch.Models; using Netch.Servers.Shadowsocks; using Netch.Servers.ShadowsocksR.Form; using Netch.Utils; -using Newtonsoft.Json.Linq; namespace Netch.Servers.ShadowsocksR { @@ -22,10 +21,7 @@ namespace Netch.Servers.ShadowsocksR public string[] UriScheme { get; } = {"ssr"}; - public Server ParseJObject(in JObject j) - { - return j.ToObject(); - } + public Type ServerType { get; } = typeof(ShadowsocksR); public void Edit(Server s) { diff --git a/Netch/Servers/Socks5/S5Util.cs b/Netch/Servers/Socks5/S5Util.cs index 3d2560b4..7440130b 100644 --- a/Netch/Servers/Socks5/S5Util.cs +++ b/Netch/Servers/Socks5/S5Util.cs @@ -4,7 +4,6 @@ using System.Linq; using Netch.Controllers; using Netch.Models; using Netch.Servers.Socks5.Form; -using Newtonsoft.Json.Linq; namespace Netch.Servers.Socks5 { @@ -20,10 +19,7 @@ namespace Netch.Servers.Socks5 public string[] UriScheme { get; } = { }; - public Server ParseJObject(in JObject j) - { - return j.ToObject(); - } + public Type ServerType { get; } = typeof(Socks5); public void Edit(Server s) { diff --git a/Netch/Servers/Trojan/TrojanController.cs b/Netch/Servers/Trojan/TrojanController.cs index f7c225e4..f999c666 100644 --- a/Netch/Servers/Trojan/TrojanController.cs +++ b/Netch/Servers/Trojan/TrojanController.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; using System.IO; +using System.Text.Json; using Netch.Controllers; using Netch.Models; using Netch.Servers.Trojan.Models; -using Newtonsoft.Json; namespace Netch.Servers.Trojan { @@ -39,13 +39,7 @@ namespace Netch.Servers.Trojan if (!string.IsNullOrWhiteSpace(server.Host)) trojanConfig.ssl.sni = server.Host; - File.WriteAllText("data\\last.json", - JsonConvert.SerializeObject(trojanConfig, - Formatting.Indented, - new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore - })); + JsonSerializer.SerializeAsync(File.Create("data\\last.json"), trojanConfig, Global.NewDefaultJsonSerializerOptions); StartInstanceAuto("-c ..\\data\\last.json"); } diff --git a/Netch/Servers/Trojan/TrojanUtil.cs b/Netch/Servers/Trojan/TrojanUtil.cs index 179b172a..a047075c 100644 --- a/Netch/Servers/Trojan/TrojanUtil.cs +++ b/Netch/Servers/Trojan/TrojanUtil.cs @@ -5,7 +5,6 @@ using System.Web; using Netch.Controllers; using Netch.Models; using Netch.Servers.Trojan.Form; -using Newtonsoft.Json.Linq; namespace Netch.Servers.Trojan { @@ -21,10 +20,7 @@ namespace Netch.Servers.Trojan public string[] UriScheme { get; } = {"trojan"}; - public Server ParseJObject(in JObject j) - { - return j.ToObject(); - } + public Type ServerType { get; } = typeof(Trojan); public void Edit(Server s) { diff --git a/Netch/Servers/V2ray/Utils/V2rayConfigUtils.cs b/Netch/Servers/V2ray/Utils/V2rayConfigUtils.cs index c2386a45..015dde6a 100644 --- a/Netch/Servers/V2ray/Utils/V2rayConfigUtils.cs +++ b/Netch/Servers/V2ray/Utils/V2rayConfigUtils.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Linq; +using System.Text.Json; using Netch.Models; using Netch.Servers.V2ray.Models; -using Newtonsoft.Json; using V2rayConfig = Netch.Servers.V2ray.Models.V2rayConfig; namespace Netch.Servers.V2ray.Utils @@ -19,9 +19,7 @@ namespace Netch.Servers.V2ray.Utils outbound(server, mode, ref v2rayConfig); - return JsonConvert.SerializeObject(v2rayConfig, - Formatting.Indented, - new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore}); + return JsonSerializer.Serialize(v2rayConfig, Global.NewDefaultJsonSerializerOptions); } private static void inbound(Server server, ref V2rayConfig v2rayConfig) diff --git a/Netch/Servers/VLESS/VLESSUtil.cs b/Netch/Servers/VLESS/VLESSUtil.cs index 3a20447b..aa746de3 100644 --- a/Netch/Servers/VLESS/VLESSUtil.cs +++ b/Netch/Servers/VLESS/VLESSUtil.cs @@ -1,8 +1,8 @@ +using System; using System.Collections.Generic; using Netch.Controllers; using Netch.Models; using Netch.Servers.V2ray; -using Newtonsoft.Json.Linq; namespace Netch.Servers.VLESS { @@ -18,10 +18,7 @@ namespace Netch.Servers.VLESS public string[] UriScheme { get; } = {"vless"}; - public Server ParseJObject(in JObject j) - { - return j.ToObject(); - } + public Type ServerType { get; } = typeof(VLESS); public void Edit(Server s) { diff --git a/Netch/Servers/VMess/VMessUtil.cs b/Netch/Servers/VMess/VMessUtil.cs index 4449881d..8bde4c4b 100644 --- a/Netch/Servers/VMess/VMessUtil.cs +++ b/Netch/Servers/VMess/VMessUtil.cs @@ -1,12 +1,13 @@ +using System; using System.Collections.Generic; +using System.Text.Encodings.Web; +using System.Text.Json; using Netch.Controllers; using Netch.Models; using Netch.Servers.V2ray; using Netch.Servers.V2ray.Models; using Netch.Servers.VMess.Form; using Netch.Utils; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace Netch.Servers.VMess { @@ -22,10 +23,7 @@ namespace Netch.Servers.VMess public string[] UriScheme { get; } = {"vmess"}; - public Server ParseJObject(in JObject j) - { - return j.ToObject(); - } + public Type ServerType { get; } = typeof(VMess); public void Edit(Server s) { @@ -43,20 +41,24 @@ namespace Netch.Servers.VMess { var server = (VMess) s; - var vmessJson = JsonConvert.SerializeObject(new - { - v = "2", - ps = server.Remark, - add = server.Hostname, - port = server.Port, - id = server.UserID, - aid = server.AlterID, - net = server.TransferProtocol, - type = server.FakeType, - host = server.Host, - path = server.Path, - tls = server.TLSSecureType - }); + var vmessJson = JsonSerializer.Serialize(new V2rayNSharing + { + v = "2", + ps = server.Remark, + add = server.Hostname, + port = server.Port.ToString(), + id = server.UserID, + aid = server.AlterID.ToString(), + net = server.TransferProtocol, + type = server.FakeType, + host = server.Host, + path = server.Path, + tls = server.TLSSecureType + }, + new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }); return "vmess://" + ShareLink.URLSafeBase64Encode(vmessJson); } @@ -76,7 +78,7 @@ namespace Netch.Servers.VMess V2rayNSharing vmess; try { - vmess = JsonConvert.DeserializeObject(ShareLink.URLSafeBase64Decode(text.Substring(8))); + vmess = JsonSerializer.Deserialize(ShareLink.URLSafeBase64Decode(text.Substring(8))); } catch { diff --git a/Netch/Utils/Configuration.cs b/Netch/Utils/Configuration.cs index 280dfc65..fb83c4ab 100644 --- a/Netch/Utils/Configuration.cs +++ b/Netch/Utils/Configuration.cs @@ -1,8 +1,8 @@ -using System.IO; +using System; +using System.IO; using System.Linq; +using System.Text.Json; using Netch.Models; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace Netch.Utils { @@ -23,30 +23,9 @@ namespace Netch.Utils /// public static void Load() { - if (Directory.Exists(DATA_DIR) && File.Exists(SETTINGS_JSON)) + if (File.Exists(SETTINGS_JSON)) { - try - { - var settingJObject = (JObject) JsonConvert.DeserializeObject(File.ReadAllText(SETTINGS_JSON))!; - Global.Settings = settingJObject?.ToObject() ?? new Setting(); - Global.Settings.Server.Clear(); - - if (settingJObject?["Server"] != null) - foreach (JObject server in settingJObject["Server"]!) - { - var serverResult = ServerHelper.ParseJObject(server); - if (serverResult != null) - Global.Settings.Server.Add(serverResult); - } - - if (settingJObject?["Profiles"] != null && Global.Settings.Profiles.Any() && - settingJObject["Profiles"]!.First()?["Index"] == null) - foreach (var profile in Global.Settings.Profiles) - profile.Index = Global.Settings.Profiles.IndexOf(profile); - } - catch (JsonException) - { - } + Global.Settings = ParseSetting(File.ReadAllText(SETTINGS_JSON)); } else { @@ -58,6 +37,34 @@ namespace Netch.Utils } } + public static Setting ParseSetting(string text) + { + try + { + var jsonSerializerOptions = Global.NewDefaultJsonSerializerOptions; + jsonSerializerOptions.Converters.Add(new ServerConverterWithTypeDiscriminator()); + var settings = JsonSerializer.Deserialize(text, jsonSerializerOptions)!; + + #region Check Profile + + foreach (var profile in settings.Profiles.Where(p => p.ServerRemark == string.Empty || p.ModeRemark == string.Empty)!) + settings.Profiles.Remove(profile); + + if (settings.Profiles.Any(p => settings.Profiles.Any(p1 => p1 != p && p1.Index == p.Index))) + for (var i = 0; i < settings.Profiles.Count; i++) + settings.Profiles[i].Index = i; + + #endregion + + return settings; + } + catch (Exception e) + { + Logging.Error(e.ToString()); + return new Setting(); + } + } + /// /// 保存配置 /// @@ -66,13 +73,7 @@ namespace Netch.Utils if (!Directory.Exists(DATA_DIR)) Directory.CreateDirectory(DATA_DIR); - File.WriteAllText(SETTINGS_JSON, - JsonConvert.SerializeObject(Global.Settings, - Formatting.Indented, - new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore - })); + File.WriteAllBytes(SETTINGS_JSON, JsonSerializer.SerializeToUtf8Bytes(Global.Settings, Global.NewDefaultJsonSerializerOptions)); } } } \ No newline at end of file diff --git a/Netch/Utils/ServerConverterWithTypeDiscriminator.cs b/Netch/Utils/ServerConverterWithTypeDiscriminator.cs new file mode 100644 index 00000000..514db08e --- /dev/null +++ b/Netch/Utils/ServerConverterWithTypeDiscriminator.cs @@ -0,0 +1,32 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Netch.Models; + +namespace Netch.Utils +{ + public class ServerConverterWithTypeDiscriminator : JsonConverter + { + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Server); + + public override Server Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var jsonElement = JsonSerializer.Deserialize(ref reader)!; + + try + { + var type = ServerHelper.GetTypeByTypeName(jsonElement.GetProperty("Type").GetString()!); + return (Server) JsonSerializer.Deserialize(jsonElement.GetRawText(), type)!; + } + catch + { + return JsonSerializer.Deserialize(jsonElement.GetRawText(), new JsonSerializerOptions())!; + } + } + + public override void Write(Utf8JsonWriter writer, Server value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, options); + } + } +} \ No newline at end of file diff --git a/Netch/Utils/ServerHelper.cs b/Netch/Utils/ServerHelper.cs index 86add0c1..6973bef9 100644 --- a/Netch/Utils/ServerHelper.cs +++ b/Netch/Utils/ServerHelper.cs @@ -5,7 +5,6 @@ using System.Reflection; using System.Threading.Tasks; using System.Timers; using Netch.Models; -using Newtonsoft.Json.Linq; namespace Netch.Utils { @@ -20,6 +19,11 @@ namespace Netch.Utils ServerUtils = serversUtilsTypes.Select(t => (IServerUtil) Activator.CreateInstance(t)).OrderBy(util => util.Priority); } + public static Type GetTypeByTypeName(string typeName) + { + return ServerUtils.Single(i => i.TypeName.Equals(typeName)).ServerType; + } + #region Delay public static class DelayTestHelper @@ -98,22 +102,6 @@ namespace Netch.Utils public static readonly IEnumerable ServerUtils; - public static Server? ParseJObject(JObject o) - { - try - { - var type = (string) o["Type"]!; - var handle = GetUtilByTypeName(type); - - return handle.ParseJObject(o); - } - catch (Exception e) - { - Console.WriteLine($"读取服务器错误: {e}\n错误项: {o}"); - return null; - } - } - public static IServerUtil GetUtilByTypeName(string typeName) { if (string.IsNullOrEmpty(typeName)) diff --git a/Netch/Utils/ShareLink.cs b/Netch/Utils/ShareLink.cs index d7ff964f..4c160275 100644 --- a/Netch/Utils/ShareLink.cs +++ b/Netch/Utils/ShareLink.cs @@ -3,11 +3,10 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using System.Text.Json; using Netch.Models; using Netch.Servers.Shadowsocks; using Netch.Servers.Shadowsocks.Models; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace Netch.Utils { @@ -33,7 +32,7 @@ namespace Netch.Utils try { - list.AddRange(JsonConvert.DeserializeObject>(text) + list.AddRange(JsonSerializer.Deserialize>(text) .Select(server => new Shadowsocks { Hostname = server.server, @@ -45,7 +44,7 @@ namespace Netch.Utils PluginOption = server.plugin_opts })); } - catch (JsonReaderException) + catch (JsonException) { foreach (var line in text.GetLines()) list.AddRange(ParseUri(line)); @@ -105,21 +104,28 @@ namespace Netch.Utils private static Server ParseNetchUri(string text) { - text = text.Substring(8); + text = URLSafeBase64Decode(text.Substring(8)); - var jObject = (JObject) JsonConvert.DeserializeObject(URLSafeBase64Decode(text))!; + var NetchLink = JsonSerializer.Deserialize(text); - var type = (string) jObject["Type"]!; - var s = ServerHelper.GetUtilByTypeName(type).ParseJObject(jObject); - return ServerHelper.GetUtilByTypeName(s.Type).CheckServer(s) ? s : throw new FormatException(); + if (string.IsNullOrEmpty(NetchLink.GetProperty("Hostname").GetString())) + throw new FormatException(); + + if (!ushort.TryParse(NetchLink.GetProperty("Port").GetString(), out _)) + throw new FormatException(); + + return JsonSerializer.Deserialize(text, + new JsonSerializerOptions + { + Converters = {new ServerConverterWithTypeDiscriminator()} + })!; } public static string GetNetchLink(Server s) { - var server = (Server) s.Clone(); - return "Netch://" + - URLSafeBase64Encode(JsonConvert.SerializeObject(server, - new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore})); + var jsonSerializerOptions = Global.NewDefaultJsonSerializerOptions; + jsonSerializerOptions.WriteIndented = false; + return "Netch://" + URLSafeBase64Encode(JsonSerializer.Serialize(s, jsonSerializerOptions)); } #region Utils diff --git a/Netch/Utils/i18N.cs b/Netch/Utils/i18N.cs index dd053bd6..1ad8e74a 100644 --- a/Netch/Utils/i18N.cs +++ b/Netch/Utils/i18N.cs @@ -4,9 +4,9 @@ using System.Globalization; using System.IO; using System.Linq; using System.Text; +using System.Text.Json; using System.Windows.Forms; using Netch.Properties; -using Newtonsoft.Json; namespace Netch.Utils { @@ -57,7 +57,7 @@ namespace Netch.Utils break; } - var dictionary = JsonConvert.DeserializeObject>(text); + var dictionary = JsonSerializer.Deserialize>(text); if (!dictionary.Any()) { diff --git a/UnitTest/TestBase.cs b/UnitTest/TestBase.cs index d0a34405..bcf2da08 100644 --- a/UnitTest/TestBase.cs +++ b/UnitTest/TestBase.cs @@ -1,14 +1,14 @@ -using Netch; -using Newtonsoft.Json; +using System.Text.Json; +using Netch; namespace UnitTest { public class TestBase { - private readonly JsonSerializerSettings _serializerSettings = new() + private readonly JsonSerializerOptions _serializerSettings = new() { - Formatting = Formatting.Indented, - NullValueHandling = NullValueHandling.Ignore + WriteIndented = true, + IgnoreNullValues = true }; protected TestBase() @@ -20,7 +20,7 @@ namespace UnitTest protected string JsonSerializerFormatted(object o) { - return JsonConvert.SerializeObject(o, _serializerSettings); + return JsonSerializer.Serialize(o, _serializerSettings); } } } \ No newline at end of file diff --git a/UnitTest/UnitTest.csproj b/UnitTest/UnitTest.csproj index de79d4e1..0e7d9245 100644 --- a/UnitTest/UnitTest.csproj +++ b/UnitTest/UnitTest.csproj @@ -5,6 +5,7 @@ x64 true latest + true @@ -24,6 +25,7 @@ +