{"id":29831932,"url":"https://github.com/machibuse/porticle.grpc.typemapper","last_synced_at":"2026-04-19T06:47:09.050Z","repository":{"id":303431903,"uuid":"1014785315","full_name":"Machibuse/Porticle.Grpc.TypeMapper","owner":"Machibuse","description":"User nullable String and Guid properties with GRPC in c#","archived":false,"fork":false,"pushed_at":"2026-03-27T17:52:02.000Z","size":89,"stargazers_count":1,"open_issues_count":1,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-19T06:46:03.610Z","etag":null,"topics":["csharp","grpc","guid","nullable-reference-types","protobuf"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Machibuse.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-06T12:14:12.000Z","updated_at":"2026-03-27T17:52:06.000Z","dependencies_parsed_at":"2025-07-23T23:10:42.275Z","dependency_job_id":null,"html_url":"https://github.com/Machibuse/Porticle.Grpc.TypeMapper","commit_stats":null,"previous_names":["machibuse/porticle.grpc.guidmapper","machibuse/porticle.grpc.typemapper"],"tags_count":36,"template":false,"template_full_name":null,"purl":"pkg:github/Machibuse/Porticle.Grpc.TypeMapper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Machibuse%2FPorticle.Grpc.TypeMapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Machibuse%2FPorticle.Grpc.TypeMapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Machibuse%2FPorticle.Grpc.TypeMapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Machibuse%2FPorticle.Grpc.TypeMapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Machibuse","download_url":"https://codeload.github.com/Machibuse/Porticle.Grpc.TypeMapper/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Machibuse%2FPorticle.Grpc.TypeMapper/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31997804,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T20:23:30.271Z","status":"online","status_checked_at":"2026-04-19T02:00:07.110Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","grpc","guid","nullable-reference-types","protobuf"],"created_at":"2025-07-29T11:12:07.816Z","updated_at":"2026-04-19T06:47:09.038Z","avatar_url":"https://github.com/Machibuse.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Porticle.Grpc.TypeMapper\n\nA Roslyn-based post-processor for protoc-generated files that adds automatic mappings for `Guid`, `Guid?`, `decimal`, `decimal?`, `string?`, nullable enums and nullable reference\ntypes. By simply adding this\npackage and adding\ncomments to\nyour `.proto` file.\n\n## Build State\n\n[![Build and Release](https://github.com/Machibuse/Porticle.Grpc.TypeMapper/actions/workflows/release.yaml/badge.svg)](https://github.com/Machibuse/Porticle.Grpc.TypeMapper/actions/workflows/release.yaml)\n\n## Nuget\n\n[![NuGet Latest Version](https://img.shields.io/nuget/v/Porticle.Grpc.TypeMapper.svg)](https://www.nuget.org/packages/Porticle.Grpc.TypeMapper/)  [![NuGet Downloads](https://img.shields.io/nuget/dt/Porticle.Grpc.TypeMapper.svg)](https://www.nuget.org/packages/Porticle.Grpc.TypeMapper/)\n\n## Overview\n\nThis library adds automatic conversion for:\n\n- Protobuf string to C# Guid\n- Protobuf google.Protobuf.StringValue to C# Guid?\n- Protobuf string to C# decimal\n- Protobuf google.Protobuf.StringValue to C# decimal?\n- Protobuf google.Protobuf.StringValue to C# string?\n- Protobuf optional enum to C# nullable enum\n- Full nullable reference type support per message via `[NullableReferenceTypes]`\n\nThis Library adds a Roslyn Postprocessing zu the c# files generated by the protoc compiler.\nEnabling seamless integration of Guid, Guid?, decimal, decimal?, string?, nullable Enums and `NRT Support` in your gRPC services without manual conversion.\nCode.\n\n## TL/DR\n\n- Add `// [GrpcGuid]` as comment to a string or StringValue proto field to get Guid/Guid? in generated c# code\n- Add `// [Decimal]` as comment to a string or StringValue proto field to get decimal/decimal? in generated c# code\n- Add `// [NullableString]` as comment to a string or StringValue proto field to get string? in generated c# code\n- Add `// [NullableEnum]` as comment to an optional enum proto field to get MyEnum? in generated c# code\n- Add `// [NullableReferenceTypes]` as comment above a message to wrap all reference type properties with `#nullable enable/disable` and make nullable ones `string?` / `TypeName?`\n- Add PorticleGrpcTypeMapper_WrapAllNonNullableStrings as Property to your Project to wrap all not nullable proto stings in #nullable enable/disable\n- Add PorticleGrpcTypeMapper_WrapAllNullableStrings as Property to your Project to wrap all proto StringValue fields in #nullable enable/disable and change `string` to `string?`\n\n\n## Installation\n\n### Install the package via NuGet:\n\n```powershell\ndotnet add package Porticle.Grpc.TypeMapper\n```\n\nAfter installing the Package, this Post build step ist dynamically added to your build.\n\n```msbuild\n\n\u003cProject\u003e\n  \u003cTarget Name=\"RunProtoPostProcessing\" AfterTargets=\"Protobuf_Compile\"\u003e\n    \u003cItemGroup\u003e\n      \u003c_FilesToPostProcess Include=\"$(MSBuildProjectDirectory)\\%(Protobuf_Compile.OutputDir)\\%(Protobuf_Compile.Filename)\"/\u003e\n    \u003c/ItemGroup\u003e\n    \u003cMessage Text=\"Proto Postprocessing\" Importance=\"high\"/\u003e\n    \u003cExec Command=\"dotnet \u0026quot;$(MSBuildThisFileDirectory)..\\tools\\$(TargetFramework)\\Porticle.Grpc.TypeMapper.dll\u0026quot; -- %(_FilesToPostProcess.Identity)\" Condition=\"'@(_FilesToPostProcess)' != ''\"/\u003e\n  \u003c/Target\u003e\n\u003c/Project\u003e\n```\n\nDon't wonder ist you cant se it in your csproj file. It is dynamically added when your build is processed.\n\n## Usage\n\nThere are several things you can do in your .proto files:\n\n- Add `// [GrpcGuid]` as comment to a string field - Converts the corresponding c# string property to Guid\n- Add `// [GrpcGuid]` as comment to a StringValue field - Converts the corresponding c# string property to Guid?\n- Add `// [Decimal]` as comment to a string field - Converts the corresponding c# string property to decimal\n- Add `// [Decimal]` as comment to a StringValue field - Converts the corresponding c# string property to decimal?\n- Add `// [NullableString]` as comment to a StringValue field - Converts the corresponding c# string property to string?\n- Add `// [NullableEnum]` as comment to a optional enum field - Converts the corresponding optional proto enum to a C# nullable Enum\n- Add `// [NullableReferenceTypes]` as comment above a message definition - Enables full nullable reference type support for the entire message (see below)\n\nFirst an Example of a default .proto file\n\n### Without TypeMapper\n\n```protobuf\nsyntax = \"proto3\";\n\nimport \"google/protobuf/wrappers.proto\";\n\nenum TestEnum {\n  FOO = 0;\n  BAR = 1;\n}\n\nmessage User {\n  // Guid of the user object   \n  string id = 1;\n\n  // Optional parent UserId\n  google.protobuf.StringValue optional_parent_user_id = 2;\n\n  // Optional description\n  google.protobuf.StringValue description = 3;\n\n  // List of roles \n  repeated string role_ids = 4;\n\n    // Price of the item\n    string price = 6;\n\n    // Simple Enum\n  optional TestEnum foo_bar = 5;\n}\n```\n\nWill result in protoc generated code like this, everything is a string or a simple enum\n\n```csharp\n/// \u003csummary\u003eGuid of the user object\u003c/summary\u003e\npublic string Id {\n  get { return id_; }\n  set { id_ = pb::ProtoPreconditions.CheckNotNull(value, \"value\"); }\n}\n\n/// \u003csummary\u003eOptional Guid of the parent UserId\u003c/summary\u003e\npublic string OptionalParentUserId {\n  get { return optionalParentUserId_; }\n  set { optionalParentUserId_ = value; }\n}\n\n/// \u003csummary\u003eOptional description string\u003c/summary\u003e\npublic string Description {\n  get { return description_; }\n  set { description_ = value; }\n}\n\n/// \u003csummary\u003eList of roles\u003c/summary\u003e\npublic pbc::RepeatedField\u003cstring\u003e RoleIds {\n  get { return roleIds_; }\n}\n\n/// \u003csummary\u003ePrice of the item\u003c/summary\u003e\npublic string Price {\n  get { return price_; }\n  set { price_ = pb::ProtoPreconditions.CheckNotNull(value, \"value\"); }\n}\n\npublic global::Porticle.Grpc.UnitTests.TestEnum FooBar {\n  get {\n\tif ((_hasBits0 \u0026 1) != 0)\n\t  return fooBar_;\n\telse\n\t  return FooBarDefaultValue;\n  }\n  set {\n\t_hasBits0 |= 1;\n\tfooBar_ = value;\n  }\n}\n```\n\n### Now a sample with TypeMapper enabled\n\n```protobuf\nsyntax = \"proto3\";\n\nimport \"google/protobuf/wrappers.proto\";\n\nenum TestEnum {\n  FOO = 0;\n  BAR = 1;\n}\n\nmessage User {\n  // [GrpcGuid] Guid of the user object   \n  string id = 1;\n\n  // [GrpcGuid] Optional Guid of the parent UserId\n  google.protobuf.StringValue optional_parent_user_id = 2;\n\n  // [NullableString] Optional description string\n  google.protobuf.StringValue description = 3;\n\n  // [GrpcGuid] List of roles \n  repeated string role_ids = 4;\n\n    // [Decimal] Price of the item\n    string price = 6;\n\n    // [NullableEnum] Simple Enum\n  optional TestEnum foo_bar = 5;\n}\n```\n\nWill result in generated code like this, using string?, Guid, Guid?, decimal and a nullable Enum\n\n```csharp\n/// \u003csummary\u003e[GrpcGuid] Guid of the user object\u003c/summary\u003e\npublic global::System.Guid Id {\n  get \n  {\n      // Parse the internal string as Guid and return it \n      return global::System.Guid.Parse(id_);\n  }\n  set \n  {\n      // Set the internal string fron the given guid\n      id_ = (value).ToString(\"D\");\n  }\n}\n\n/// \u003csummary\u003e[GrpcGuid] Optional Guid of the parent UserId\u003c/summary\u003e\npublic global::System.Guid? OptionalParentUserId {\n  get { \n      // return null wehn corresponding string is null\n      if(optionalParentUserId_==null) return default;\n      // return a Guid instead of the string\n      return global::System.Guid.Parse(optionalParentUserId_); \n  }\n  set \n  {\n      // sets the internal string from the given guid\n      optionalParentUserId_ = (value)?.ToString(\"D\");\n  }\n}\n\n\n// enable nullable for this property and return string? instead of string \n#nullable enable\n/// \u003csummary\u003e[NullableString] Optional description string\u003c/summary\u003e\npublic string? Description {\n  get { return description_; }\n  set { description_ = value; }\n}\n#nullable disable\n\n/// \u003csummary\u003e[GrpcGuid] List of roles\u003c/summary\u003e\npublic IList\u003cGuid\u003e RoleIds {\n  get \n  {\n      // returns a wrapper that converts a list of strings to a list of guids \n      return new RepeatedFieldGuidWrapper(roleIds_);\n  }\n}\n\n/// \u003csummary\u003e[Decimal] Price of the item\u003c/summary\u003e\npublic decimal Price {\n  get\n  {\n      // Parse the internal string as decimal using InvariantCulture\n      return decimal.Parse(price_, System.Globalization.CultureInfo.InvariantCulture);\n  }\n  set\n  {\n      // Set the internal string from the given decimal\n      price_ = (value).ToString(System.Globalization.CultureInfo.InvariantCulture);\n  }\n}\n\npublic global::Porticle.Grpc.UnitTests.TestEnum? FooBar {\n  get { \n\tif ((_hasBits0 \u0026 1) != 0) \n\t{ \n\t  return fooBar_; \n\t} \n\telse \n\t{ \n      // return null instead of default value when Has-Flag is false  \n\t  return null; \n    } \n  }\n  set { \n\tif(value==null) {\n        // Set hasflag to false when null is assigend   \n\t\t_hasBits0 \u0026=~1;\n\t\tfooBar_ = FooBarDefaultValue;\n\t}\n\telse\n\t{\n\t\t_hasBits0 |= 1;\n\t\tfooBar_ = value.Value;\n\t} \n  }\n}\n```\n\n## `[NullableReferenceTypes]` - Per-Message Nullable Reference Types\n\nAdd `// [NullableReferenceTypes]` as a comment above a `message` definition to automatically apply nullable reference type annotations to **all** properties in that message:\n\n1. **Non-nullable strings** (`string` with `ProtoPreconditions.CheckNotNull`) are wrapped in `#nullable enable/disable` so the compiler knows they must not be null\n2. **Nullable strings** (`google.protobuf.StringValue`) are changed to `string?` and wrapped in `#nullable enable/disable`\n3. **Message references** (sub-messages) are changed to `TypeName?` and wrapped in `#nullable enable/disable`\n\nScalar types (`int`, `double`, `bool`, etc.), enums, repeated fields, and maps are **not** affected.\n\n### Example\n\n```protobuf\nsyntax = \"proto3\";\n\nimport \"google/protobuf/wrappers.proto\";\n\n// [NullableReferenceTypes]\nmessage User {\n    string name = 1;\n    google.protobuf.StringValue description = 2;\n    Address address = 3;\n    int32 age = 4;\n}\n\nmessage Address {\n    string street = 1;\n}\n```\n\nWill result in generated code like this:\n\n```csharp\n#nullable enable\npublic string Name {\n  get { return name_; }\n  set { name_ = pb::ProtoPreconditions.CheckNotNull(value, \"value\"); }\n}\n#nullable disable\n\n#nullable enable\npublic string? Description {\n  get { return description_; }\n  set { description_ = value; }\n}\n#nullable disable\n\n#nullable enable\npublic global::MyNamespace.Address? Address {\n  get { return address_; }\n  set { address_ = value; }\n}\n#nullable disable\n\n// int is a value type - not affected\npublic int Age {\n  get { return age_; }\n  set { age_ = value; }\n}\n```\n\n## Support for 'Nullable Reference Types' (Project-wide Settings)\n\nThere are actually 2 Settings for Nullable Reference Types you can set in your Project file, or better in Directory.Build.props\n\n```XML\n\u003cProject\u003e\n    \u003cPropertyGroup\u003e\n        \u003cPorticleGrpcTypeMapper_WrapAllNonNullableStrings\u003etrue\u003c/PorticleGrpcTypeMapper_WrapAllNonNullableStrings\u003e\n        \u003cPorticleGrpcTypeMapper_WrapAllNullableStringValues\u003etrue\u003c/PorticleGrpcTypeMapper_WrapAllNullableStringValues\u003e\n    \u003c/PropertyGroup\u003e\n\u003c/Project\u003e\n```\n\n### PorticleGrpcTypeMapper_WrapAllNonNullableStrings\n\nThis means that all Strings like this \n\n```protobuf\nsyntax = \"proto3\";\n\nmessage Foo {\n  string normal_string = 1;\n}\n```\n\nWill be wrapped in '#nullable enable/disable' like this\n\n```csharp\n#nullable enable\n\npublic string NormalString {\n  get { return normalString_; }\n  set { id_ = pb::ProtoPreconditions.CheckNotNull(value, \"value\"); }\n}\n\n#nullable disable\n```\n\n### PorticleGrpcTypeMapper_WrapAllNullableStringValues\n\nThis means that all Strings like this \n\n```protobuf\nsyntax = \"proto3\";\n\nimport \"google/protobuf/wrappers.proto\";\n\nmessage Foo {\n  google.protobuf.StringValue nullable_string = 1;\n}\n```\n\nWill be wrapped in '#nullable enable/disable' like this and string is changed to string?\n\n```csharp\n#nullable enable\n\npublic string? OptionalParentUserId {\n  get { return optionalParentUserId_; }\n  set { optionalParentUserId_ = value; }\n}\n\n#nullable disable\n```\n\n## What is currently not Possible?\n\n- Mapping `repeated google.protobuf.StringValue` to `List\u003cGuid?\u003e` or `List\u003cstring?\u003e` because grpc internally uses `RepeatedField\u003cstring\u003e` instead of `RepeatedField\u003cStringValue\u003e`.\n  This may be a bug in protoc compiler, because it is also not possible to add `null` to `repeated google.protobuf.StringValue` because there is a not null check in the Add\n  function in `RepeatedField\u003cT\u003e`\n\n- This Tool actually is not tested with protoc / Grpc.Tools is compiled with GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE flag\n\n- All Messages implements ICustomDiagnisticMessage to prevent GRPC ToDiagnbosticString because ist crashes when Guids are converted to Strings  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmachibuse%2Fporticle.grpc.typemapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmachibuse%2Fporticle.grpc.typemapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmachibuse%2Fporticle.grpc.typemapper/lists"}