• 如何在C#中使用Google.Protobuf工具


      protobuf是一个语言无关、平台无关的序列化协议,由谷歌开源提供。再加上其高性能、存储占用更小等特点,在云原生的应用中越来越广泛。
    在C#中主要有两种方法来使用protobuf协议,nuget包分别为Google.Protobuf和protobuf-net,其中Google.Protobuf由谷歌官方提供。本文简要记录和展示Google.Protobuf的使用方法和特点。

    项目资料及文档

    • 项目官网:https://developers.google.cn/protocol-buffers?hl=zh-cn
    • github主页:https://github.com/protocolbuffers/protobuf/
    • 官方文档:https://developers.google.cn/protocol-buffers/docs/overview?hl=zh-cn
    • 该nuget包支持.NETFramework 4.5、.NETStandard1.1、.net5等

    准备工作

      需要用到的nuget有如下两个:Google.Protobuf、Google.Protobuf.Tools,其中Google.Protobuf是主类库,运行时要用到。Google.Protobuf.Tools提供了命令行工具,用于根据.proto文件转为目标语言的类型,仅开发时使用,运行时不需要。
      本次Demo使用的.proto文件内容如下:
    syntax = "proto3";
    option cc_enable_arenas = true;
    
    package Tccc.Demo.Protobuf;
    
    message ErrorLog {
        string LogID = 1;
        string Context = 2;
        string Stack = 3;
    }
    PS:更多数据类型的映射关系可以查看:Protobuf 标量数据类型 - 适用于 WCF 开发人员的 gRPC | Microsoft Docs
     
    首先需要根据.proto文件生成目标类型,操作如下:
    ./google.protobuf.tools\3.19.1\tools\windows_x64\protoc.exe --csharp_out=./generatedCode ./proto/ErrorLog.proto

      其中--csharp_out选项是生成C#语言的目标类型,运行protoc.exe -h 查看帮助信息,可以看到还支持一下几种选项: 

    --proto_path=PATH
    --cpp_out=OUT_DIR Generate C++ header and source.
    --csharp_out=OUT_DIR Generate C# source file.
    --java_out=OUT_DIR Generate Java source file.
    --js_out=OUT_DIR Generate JavaScript source.
    --kotlin_out=OUT_DIR Generate Kotlin file.
    --objc_out=OUT_DIR Generate Objective-C header and source.
    --php_out=OUT_DIR Generate PHP source file.
    --python_out=OUT_DIR Generate Python source file.
    --ruby_out=OUT_DIR Generate Ruby source file.
      运行上述命令,会根据指定的ErrorLog.proto文件生成ErrorLog.cs文件,文件中就是C#类型ErrorLog。生成的代码中会给此类型增加方法void WriteTo(CodedOutputStream output)和只读属性Parser,接下来进行序列化和反序列化的关键。
     
    生成的ErrorLog类的完整代码:
    // <auto-generated>
    //     Generated by the protocol buffer compiler.  DO NOT EDIT!
    //     source: ProtoFiles/ErrorLog.proto
    // </auto-generated>
    #pragma warning disable 1591, 0612, 3021
    #region Designer generated code
    
    using pb = global::Google.Protobuf;
    using pbc = global::Google.Protobuf.Collections;
    using pbr = global::Google.Protobuf.Reflection;
    using scg = global::System.Collections.Generic;
    namespace Tccc.Demo.Protobuf {
    
      /// <summary>Holder for reflection information generated from ProtoFiles/ErrorLog.proto</summary>
      public static partial class ErrorLogReflection {
    
        #region Descriptor
        /// <summary>File descriptor for ProtoFiles/ErrorLog.proto</summary>
        public static pbr::FileDescriptor Descriptor {
          get { return descriptor; }
        }
        private static pbr::FileDescriptor descriptor;
    
        static ErrorLogReflection() {
          byte[] descriptorData = global::System.Convert.FromBase64String(
              string.Concat(
                "ChlQcm90b0ZpbGVzL0Vycm9yTG9nLnByb3RvEhJUY2NjLkRlbW8uUHJvdG9i",
                "dWYiOQoIRXJyb3JMb2cSDQoFTG9nSUQYASABKAkSDwoHQ29udGV4dBgCIAEo",
                "CRINCgVTdGFjaxgDIAEoCUID+AEBYgZwcm90bzM="));
          descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
              new pbr::FileDescriptor[] { },
              new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
                new pbr::GeneratedClrTypeInfo(typeof(global::Tccc.Demo.Protobuf.ErrorLog), global::Tccc.Demo.Protobuf.ErrorLog.Parser, new[]{ "LogID", "Context", "Stack" }, null, null, null, null)
              }));
        }
        #endregion
    
      }
      #region Messages
      public sealed partial class ErrorLog : pb::IMessage<ErrorLog>
      #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
          , pb::IBufferMessage
      #endif
      {
        private static readonly pb::MessageParser<ErrorLog> _parser = new pb::MessageParser<ErrorLog>(() => new ErrorLog());
        private pb::UnknownFieldSet _unknownFields;
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public static pb::MessageParser<ErrorLog> Parser { get { return _parser; } }
    
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public static pbr::MessageDescriptor Descriptor {
          get { return global::Tccc.Demo.Protobuf.ErrorLogReflection.Descriptor.MessageTypes[0]; }
        }
    
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        pbr::MessageDescriptor pb::IMessage.Descriptor {
          get { return Descriptor; }
        }
    
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public ErrorLog() {
          OnConstruction();
        }
    
        partial void OnConstruction();
    
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public ErrorLog(ErrorLog other) : this() {
          logID_ = other.logID_;
          context_ = other.context_;
          stack_ = other.stack_;
          _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
        }
    
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public ErrorLog Clone() {
          return new ErrorLog(this);
        }
    
        /// <summary>Field number for the "LogID" field.</summary>
        public const int LogIDFieldNumber = 1;
        private string logID_ = "";
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public string LogID {
          get { return logID_; }
          set {
            logID_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
          }
        }
    
        /// <summary>Field number for the "Context" field.</summary>
        public const int ContextFieldNumber = 2;
        private string context_ = "";
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public string Context {
          get { return context_; }
          set {
            context_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
          }
        }
    
        /// <summary>Field number for the "Stack" field.</summary>
        public const int StackFieldNumber = 3;
        private string stack_ = "";
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public string Stack {
          get { return stack_; }
          set {
            stack_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
          }
        }
    
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public override bool Equals(object other) {
          return Equals(other as ErrorLog);
        }
    
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public bool Equals(ErrorLog other) {
          if (ReferenceEquals(other, null)) {
            return false;
          }
          if (ReferenceEquals(other, this)) {
            return true;
          }
          if (LogID != other.LogID) return false;
          if (Context != other.Context) return false;
          if (Stack != other.Stack) return false;
          return Equals(_unknownFields, other._unknownFields);
        }
    
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public override int GetHashCode() {
          int hash = 1;
          if (LogID.Length != 0) hash ^= LogID.GetHashCode();
          if (Context.Length != 0) hash ^= Context.GetHashCode();
          if (Stack.Length != 0) hash ^= Stack.GetHashCode();
          if (_unknownFields != null) {
            hash ^= _unknownFields.GetHashCode();
          }
          return hash;
        }
    
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public override string ToString() {
          return pb::JsonFormatter.ToDiagnosticString(this);
        }
    
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public void WriteTo(pb::CodedOutputStream output) {
        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
          output.WriteRawMessage(this);
        #else
          if (LogID.Length != 0) {
            output.WriteRawTag(10);
            output.WriteString(LogID);
          }
          if (Context.Length != 0) {
            output.WriteRawTag(18);
            output.WriteString(Context);
          }
          if (Stack.Length != 0) {
            output.WriteRawTag(26);
            output.WriteString(Stack);
          }
          if (_unknownFields != null) {
            _unknownFields.WriteTo(output);
          }
        #endif
        }
    
        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
          if (LogID.Length != 0) {
            output.WriteRawTag(10);
            output.WriteString(LogID);
          }
          if (Context.Length != 0) {
            output.WriteRawTag(18);
            output.WriteString(Context);
          }
          if (Stack.Length != 0) {
            output.WriteRawTag(26);
            output.WriteString(Stack);
          }
          if (_unknownFields != null) {
            _unknownFields.WriteTo(ref output);
          }
        }
        #endif
    
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public int CalculateSize() {
          int size = 0;
          if (LogID.Length != 0) {
            size += 1 + pb::CodedOutputStream.ComputeStringSize(LogID);
          }
          if (Context.Length != 0) {
            size += 1 + pb::CodedOutputStream.ComputeStringSize(Context);
          }
          if (Stack.Length != 0) {
            size += 1 + pb::CodedOutputStream.ComputeStringSize(Stack);
          }
          if (_unknownFields != null) {
            size += _unknownFields.CalculateSize();
          }
          return size;
        }
    
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public void MergeFrom(ErrorLog other) {
          if (other == null) {
            return;
          }
          if (other.LogID.Length != 0) {
            LogID = other.LogID;
          }
          if (other.Context.Length != 0) {
            Context = other.Context;
          }
          if (other.Stack.Length != 0) {
            Stack = other.Stack;
          }
          _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
        }
    
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        public void MergeFrom(pb::CodedInputStream input) {
        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
          input.ReadRawMessage(this);
        #else
          uint tag;
          while ((tag = input.ReadTag()) != 0) {
            switch(tag) {
              default:
                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
                break;
              case 10: {
                LogID = input.ReadString();
                break;
              }
              case 18: {
                Context = input.ReadString();
                break;
              }
              case 26: {
                Stack = input.ReadString();
                break;
              }
            }
          }
        #endif
        }
    
        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
          uint tag;
          while ((tag = input.ReadTag()) != 0) {
            switch(tag) {
              default:
                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
                break;
              case 10: {
                LogID = input.ReadString();
                break;
              }
              case 18: {
                Context = input.ReadString();
                break;
              }
              case 26: {
                Stack = input.ReadString();
                break;
              }
            }
          }
        }
        #endif
    
      }
    
      #endregion
    
    }
    
    #endregion Designer generated code
    View Code

    序列化操作

            public static byte[] Serialize(ErrorLog log)
            {
                using (MemoryStream output = new MemoryStream())
                {
                    log.WriteTo(output);
                    return output.ToArray();
                }
            }

    反序列化操作

                ErrorLog desErrorLog= ErrorLog.Parser.ParseFrom(data);

     

    使用特点和理解

    • protoc.exe是支持生成多语言类型,这对于跨语言的混合编程比较方便。
    • 根据上述使用步骤可以看到,必须先使用工具protoc生成目标类型,才能调用序列化和反序列化方法,这有些不符合.net平台的编码习惯。
    • 一堆自动生成的C#类在可维护性方面欠佳,当需要调整属性字段时,还要通过工具重新生成,较为麻烦。

    Protobuf 标量数据类型 - 适用于 WCF 开发人员的 gRPC | Microsoft Docs

  • 相关阅读:
    "Java:comp/env/"讲解与JNDI
    table的td去边框
    jsp获取所有参数
    spring-mvc设置首页
    jdbc数据库连接方式
    文件上传
    SMBMS
    过滤器和监听器
    解决Maven的JDK版本问题
    MVC
  • 原文地址:https://www.cnblogs.com/chen943354/p/15597270.html
Copyright © 2020-2023  润新知