Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save PanosGreg/e481f21ad6b7632008381f4b3f9736a1 to your computer and use it in GitHub Desktop.

Select an option

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
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
}
## 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)
@KasperJack

Copy link
Copy Markdown

πŸ‘ πŸ‘

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment