nacos-group / nacos-sdk-csharp

This nacos csharp sdk
https://nacos-sdk-csharp.readthedocs.io
Apache License 2.0
421 stars 91 forks source link

是否考虑迁移到System.Text.Json和grpc-dotnet #258

Closed RoyLi-Pvt closed 2 weeks ago

RoyLi-Pvt commented 1 year ago

如题 现使用的Newtonsoft.Json和Grpc.Core都比较老旧且性能较差,Grpc.Core包本身也处于仅维护状态. 基于性能和面向未来的考量,应该迁移到更新的System.Text.Json和grpc-dotnet 而且这样对于较新的程序来说也不用额外引用大量陈旧的Nuget包

catcherwong commented 1 year ago

计划着在2.0版本会处理这个问题。

RoyLi-Pvt commented 1 year ago

那就盼望2.0早日Release了~~ 其实我已经完成了初步的移植,测试是全Pass的,但是由于赶时间所以修改的很凌乱不适合提PR😂. 总体上移植还是很简单的,只是需要注意一下几点

除了上述几点以外都是非常常规的改动了,替换Attribute之类的.

heqingpan commented 7 months ago

支持迁移到grpc-dotnet,这样能同时解决.net core sdk不支持m1芯片的问题 #251

China-xiaoFang commented 7 months ago

那就盼望2.0早日Release了~~ 其实我已经完成了初步的移植,测试是全Pass的,但是由于赶时间所以修改的很凌乱不适合提PR😂. 总体上移植还是很简单的,只是需要注意一下几点

  • System.Text.Json和Json.Net的默认序列化行为是不一样的所以需要给System.Text.Json额外指定一个JsonSerializerOptions来确保一致性,类似下面这样:

    internal static JsonSerializerOptions JsonOpt = new()
    {
      PropertyNameCaseInsensitive = true,
      Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
      IgnoreReadOnlyFields = false,
      DefaultIgnoreCondition = JsonIgnoreCondition.Never,
      IncludeFields = true
    };

    在ObjectUtil中需要在序列化时引入这个Option. 不过我没严格的测试具体是那个选项会影响功能实现,只是参考文档把两边的默认行为尽量修改为一致.

  • 基于System.Text.Json的API,我重写了DefaultJsonConfigurationStringParser,以下两个函数由NewBing辅助给出(包括注释😂):

      private IDictionary<string, string> ParseString(string input)
      {
          var Doc = JsonDocument.Parse(input);
          SortedDictionary<string, string> SortedDict = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
          var Pairs = this.GetKeyValuePairs(Doc.RootElement, SortedDict);
          foreach (var Pair in Pairs)
          {
              SortedDict.Add(Pair.Key, Pair.Value);
          }
          return SortedDict;
    
      }
      private IEnumerable<KeyValuePair<string, string>> GetKeyValuePairs(JsonElement element, SortedDictionary<string, string> SortedDict, string path = "")
      {
          // 如果元素是对象,则遍历其属性
          if (element.ValueKind == JsonValueKind.Object)
          {
              foreach (var property in element.EnumerateObject())
              {
                  // 拼接属性名称到路径
                  string subPath = path + "/" + property.Name;
                  // 递归调用
                  foreach (var pair in GetKeyValuePairs(property.Value, SortedDict, subPath))
                  {
                      yield return pair;
                  }
              }
          }
          // 如果元素是数组,则遍历其元素
          else if (element.ValueKind == JsonValueKind.Array)
          {
              int index = 0;
              foreach (var item in element.EnumerateArray())
              {
                  // 拼接索引到路径
                  string subPath = path + ":" + index;
                  // 递归调用
                  foreach (var pair in GetKeyValuePairs(item, SortedDict, subPath))
                  {
                      yield return pair;
                  }
                  index++;
              }
          }
          // 否则,元素是一个基本值,直接返回键值对
          else
          {
              var Pair = new KeyValuePair<string, string>(path.TrimStart('/'), element.ToString());
              yield return Pair;
          }
      }
  • grpc-dotnet不支持以new List<Grpc.Core.ChannelOption>的形式传入自定义ChannelOption(本身在grpc定义中ChannelOption也只能在可选值内选择),此外语法和远程地址的格式也有调整所以grpc初始化Channel修改为一下语句:
      Channel = GrpcChannel.ForAddress($"http://{serverInfo.ServerIp}:{port}",
              new GrpcChannelOptions { Credentials = ChannelCredentials.Insecure});

除了上述几点以外都是非常常规的改动了,替换Attribute之类的.

您好: 是否可以提供一下您从 Newtonsoft.Json 库迁移到 System.Text.Json 库的代码嘛?我们最近也在使用 Nacos,却发现原有的包中的 JSON 序列化库和我们的冲突了,导致很多时候不知道用那个。 谢谢!

RoyLi-Pvt commented 7 months ago

额 @Net-18K 迁移到 System.Text.Json的代码在你引用的那个回复里面基本上已经全提供了啊~~ 剩下的只是批量替换Json.Net的Attribute为System.Text.Json的版本或者将实际调用的函数由Json.Net切换到System.Text.Json对应实现而已

hzy-6 commented 1 month ago

那就盼望2.0早日Release了~~ 其实我已经完成了初步的移植,测试是全Pass的,但是由于赶时间所以修改的很凌乱不适合提PR😂. 总体上移植还是很简单的,只是需要注意一下几点

  • System.Text.Json和Json.Net的默认序列化行为是不一样的所以需要给System.Text.Json额外指定一个JsonSerializerOptions来确保一致性,类似下面这样:

    internal static JsonSerializerOptions JsonOpt = new()
    {
      PropertyNameCaseInsensitive = true,
      Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
      IgnoreReadOnlyFields = false,
      DefaultIgnoreCondition = JsonIgnoreCondition.Never,
      IncludeFields = true
    };

    在ObjectUtil中需要在序列化时引入这个Option. 不过我没严格的测试具体是那个选项会影响功能实现,只是参考文档把两边的默认行为尽量修改为一致.

  • 基于System.Text.Json的API,我重写了DefaultJsonConfigurationStringParser,以下两个函数由NewBing辅助给出(包括注释😂):

      private IDictionary<string, string> ParseString(string input)
      {
          var Doc = JsonDocument.Parse(input);
          SortedDictionary<string, string> SortedDict = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
          var Pairs = this.GetKeyValuePairs(Doc.RootElement, SortedDict);
          foreach (var Pair in Pairs)
          {
              SortedDict.Add(Pair.Key, Pair.Value);
          }
          return SortedDict;
    
      }
      private IEnumerable<KeyValuePair<string, string>> GetKeyValuePairs(JsonElement element, SortedDictionary<string, string> SortedDict, string path = "")
      {
          // 如果元素是对象,则遍历其属性
          if (element.ValueKind == JsonValueKind.Object)
          {
              foreach (var property in element.EnumerateObject())
              {
                  // 拼接属性名称到路径
                  string subPath = path + "/" + property.Name;
                  // 递归调用
                  foreach (var pair in GetKeyValuePairs(property.Value, SortedDict, subPath))
                  {
                      yield return pair;
                  }
              }
          }
          // 如果元素是数组,则遍历其元素
          else if (element.ValueKind == JsonValueKind.Array)
          {
              int index = 0;
              foreach (var item in element.EnumerateArray())
              {
                  // 拼接索引到路径
                  string subPath = path + ":" + index;
                  // 递归调用
                  foreach (var pair in GetKeyValuePairs(item, SortedDict, subPath))
                  {
                      yield return pair;
                  }
                  index++;
              }
          }
          // 否则,元素是一个基本值,直接返回键值对
          else
          {
              var Pair = new KeyValuePair<string, string>(path.TrimStart('/'), element.ToString());
              yield return Pair;
          }
      }
  • grpc-dotnet不支持以new List<Grpc.Core.ChannelOption>的形式传入自定义ChannelOption(本身在grpc定义中ChannelOption也只能在可选值内选择),此外语法和远程地址的格式也有调整所以grpc初始化Channel修改为一下语句:
      Channel = GrpcChannel.ForAddress($"http://{serverInfo.ServerIp}:{port}",
              new GrpcChannelOptions { Credentials = ChannelCredentials.Insecure});

除了上述几点以外都是非常常规的改动了,替换Attribute之类的.

目前有支持 grpc-tool 改良后的代码吗?我现在 mac 跑不起来

catcherwong commented 3 weeks ago

最新预览版已经完成了相关的迁移。 1.3.9-alpha20240920152555

catcherwong commented 2 weeks ago

已发布正式的 1.3.9 版本