Last active
June 26, 2026 12:32
-
-
Save PanosGreg/e481f21ad6b7632008381f4b3f9736a1 to your computer and use it in GitHub Desktop.
Simple example on how to do ser/des with Google's Protobuf in PowerShell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function Get-ProtoC { | |
| <# | |
| .SYNOPSIS | |
| Download latest protoc.exe binary from github | |
| The protoc tool is the protocol buffer compiler | |
| Source: https://github.com/protocolbuffers/protobuf/releases | |
| .EXAMPLE | |
| Get-ProtoC -Path C:\temp | |
| #> | |
| [OutputType([System.IO.FileInfo])] | |
| [cmdletbinding()] | |
| param ( | |
| [string]$Path = 'C:\Temp' | |
| ) | |
| #Requires -Modules Microsoft.PowerShell.Utility, Microsoft.PowerShell.Archive, Microsoft.PowerShell.Management | |
| # get the download url for the zip file | |
| $repo = 'protobuf' | |
| $org = 'protocolbuffers' | |
| $releases = "https://api.github.com/repos/$org/$repo/releases" | |
| $all = Invoke-RestMethod -Uri $releases -Verbose:$false | |
| $win64 = $all[0].assets | where name -like *win64.zip # <-- the first one in the list is the latest version (that's the [0] index) | |
| $zipfile = Join-Path $Path $win64.name | |
| # download the zip file | |
| if (Test-Path $zipfile) { | |
| Write-Warning "The zip file $zipfile already exists, it will be overwritten" | |
| Remove-Item -Path $zipfile -Force -Verbose:$false | |
| } | |
| $params = @{ | |
| Uri = $win64.url | |
| Headers = @{Accept='application/octet-stream'} | |
| OutFile = $zipfile | |
| Verbose = $false | |
| ErrorAction = 'Stop' | |
| } | |
| Invoke-WebRequest @params | |
| if (-not (Test-Path $zipfile)) { | |
| Write-Warning "Cannot find the downloaded file $zipfile, maybe the download had an error" | |
| return | |
| } | |
| # uncompress it | |
| $UnzipPath = $zipfile.Replace('.zip',$null) | |
| if (Test-Path $UnzipPath) {Write-Warning "The path $UnzipPath already exists" ; return} | |
| (Expand-Archive -Path $zipfile -DestinationPath $UnzipPath -Verbose:$false -EA Stop) 4>$null | |
| if (-not (Test-Path $UnzipPath)) { | |
| Write-Warning "Cannot find the unziiped folder $UnzipPath, maybe the zip extraction had errors" | |
| return | |
| } | |
| # get the .exe binary | |
| $exe = Join-Path $UnzipPath 'bin\protoc.exe' | |
| if (-not (Test-Path $exe)) { | |
| Write-Warning "Cannot find the .exe file $exe, you'll need to get it manually" | |
| return | |
| } | |
| $FinalExe = (Join-Path $Path 'protoc.exe') | |
| if (Test-Path $FinalExe) { | |
| Write-Verbose "The .exe file $FinalExe already exists, it will be overwritten" | |
| } | |
| Copy-Item -Path $exe -Destination $Path -Verbose:$false -Force -ErrorAction Stop | |
| # remove the unzipped folder | |
| Remove-Item -Path $UnzipPath -Recurse -Force -Verbose:$false -ErrorAction Stop | |
| if (Test-Path $UnzipPath) { | |
| Write-Warning "Could not remove the unzipped folder $UnzipPath, you'll need to delete it manually" | |
| } | |
| # remove the zip file | |
| Remove-Item -Path $zipfile -Force -Verbose:$false -ErrorAction Stop | |
| # finally show the output | |
| Get-Item $FinalExe | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ## Protobuf Serialization & Deserialization | |
| # This is the process to serialize and deserialize data into/from protobuf | |
| # Protobuf is supposed to be much smaller than JSON or XML and that's its main advantage | |
| # Date: 09-Jun-2026 | |
| # you will need to following thing: | |
| # - nuget tool (ex. scoop install nuget) | |
| # the following was done in PowerShell 7.6.2 on Windows 11 | |
| # which means we are running on a .NET v10.0 runtime (that is PS v7.6) | |
| ## ** Initial Setup ** | |
| # go to a sample folder (create one if need be) | |
| cd (md C:\temp\proto) | |
| # download the protoc tool (that's the proto compiler) | |
| # get it from: https://github.com/protocolbuffers/protobuf/releases | |
| . C:\temp\Get-ProtoC.ps1 | |
| Get-ProtoC -Path C:\temp\proto | |
| # create a sample class | |
| @' | |
| syntax = "proto3"; | |
| package example; | |
| option csharp_namespace = "MyCustomProto"; | |
| message Person { | |
| string name = 1; | |
| int32 id = 2; | |
| string email = 3; | |
| } | |
| '@ | Out-File person.proto | |
| # extended example: https://protobuf.dev/getting-started/csharptutorial/#protocol-format | |
| # generate the .cs file from that .proto file | |
| $DST_DIR = 'C:\temp\proto' | |
| .\protoc --proto_path=. --csharp_out="$DST_DIR" .\person.proto | |
| # Note: this will generate the C# file c:\temp\proto\Person.cs | |
| # download the Google.Protobuf library | |
| cd (md ProtobufLibs) | |
| nuget install Google.Protobuf | |
| cd .. | |
| <# This will install a number of packages (versions as of Jun-2026) | |
| Google.Protobuf.3.35.0 | |
| System.Buffers.4.4.0 | |
| System.Memory.4.5.3 | |
| System.Numerics.Vectors.4.4.0 | |
| System.Runtime.CompilerServices.Unsafe.4.5.2 | |
| But since we are on .NET 6+ we only need the Google.Protobuf folder | |
| #> | |
| # load the google library | |
| $gDll = (dir 'ProtobufLibs\Google*\lib\net5.0\*.dll').FullName | |
| Add-Type -Path $gDll | |
| if (-not 'Google.Protobuf.IMessage' -as [type]) {Write-Warning 'Cannot find the Protobuf classes!!'} | |
| # now load our library | |
| $ref = [Google.Protobuf.MessageParser].Assembly.FullName | |
| Add-Type -Path .\Person.cs -ReferencedAssemblies $ref -IgnoreWarnings -WarningAction Ignore | |
| ## ** Serialization ** | |
| # you can finally use our library | |
| $Person = [MyCustomProto.Person]@{ | |
| Name = 'some_name' | |
| Id = 12 | |
| Email = 'some_email@domain.com' | |
| } | |
| # convert the data into a byte array | |
| $MemStream = [System.IO.MemoryStream]::new() | |
| $ProtoStream = [Google.Protobuf.CodedOutputStream]::new($MemStream) | |
| $Person.WriteTo($ProtoStream) | |
| $ProtoStream.Flush() | |
| $SerializedBytes = $MemStream.ToArray() | |
| # Note: the method $Person.ToByteArray() was not found, hence why I'm using a memory stream | |
| # save the byte array into a file | |
| $Path = Join-Path $PWD person.dat | |
| $File = [System.IO.File]::Create($Path) | |
| $File.Write($SerializedBytes,0,$SerializedBytes.Length) | |
| $File.Close() | |
| # clean up (order matters here) | |
| $ProtoStream.Dispose() | |
| $MemStream.Dispose() | |
| $File.Dispose() | |
| # check the size of the file, to see how small it is (compared to json or xml) | |
| Get-Item $Path | select Name,Length | |
| # you can now send that data anywhere you want | |
| <# by now you have the following files in the c:\temp\proto folder | |
| - Person.cs | |
| - person.dat | |
| - person.proto | |
| - protoc.exe | |
| And the \ProtobufLibs sub directory | |
| #> | |
| ## ** Deserialization ** | |
| # To properly check the deserialization, we can open up a new powershell console | |
| # to simulate a different environment | |
| # load the google library | |
| cd C:\temp\proto | |
| $gDll = (dir 'ProtobufLibs\Google*\lib\net5.0\*.dll').FullName | |
| Add-Type -Path $gDll | |
| # load up our class | |
| $ref = [Google.Protobuf.MessageParser].Assembly.FullName | |
| Add-Type -Path .\Person.cs -ReferencedAssemblies $ref -IgnoreWarnings -WarningAction Ignore | |
| # Get the parser for the Person message | |
| $Parser = [MyCustomProto.Person]::Parser | |
| # get the byte array from our data | |
| $Path = 'c:\temp\proto\person.dat' | |
| $ByteData = [System.IO.File]::ReadAllBytes($Path) # <-- this needs a full path, not relative path | |
| # Deserialize the byte array back into a Person object | |
| $DeserializedPerson = $Parser.ParseFrom($ByteData) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
π π