Let's assume you have protobuf proto files, generated C# message classes for them, and you want to generate fake data for demo purposes. You can use Google.Protobuf.Reflection
for it as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using Google.Protobuf;
using Google.Protobuf.Reflection;
using Google.Protobuf.Collections;
using Google.Protobuf.WellKnownTypes;
using System.Collections;
public class ProtobufFaker {
private readonly Random _random;
private readonly int _maxCollectionSize;
private readonly int _stringMaxLength;
public ProtobufFaker(int? seed = null, int maxCollectionSize = 5, int stringMaxLength = 20) {
_random = seed.HasValue ? new Random(seed.Value) : new Random();
_maxCollectionSize = maxCollectionSize;
_stringMaxLength = stringMaxLength;
}
public T Generate<T>() where T : IMessage, new() {
var message = new T();
PopulateMessage(message);
return message;
}
public void PopulateMessage(IMessage message) {
var descriptor = message.Descriptor;
foreach (var fieldDescriptor in descriptor.Fields.InDeclarationOrder()) {
if (fieldDescriptor.IsRepeated) {
var list = (IList)fieldDescriptor.Accessor.GetValue(message);
var count = _random.Next(1, _maxCollectionSize + 1);
for (int i = 0; i < count; i++) {
var value = GenerateSingleValue(fieldDescriptor);
if (value != null) {
list.Add(value);
}
}
}
else {
var value = GenerateFieldValue(fieldDescriptor);
if (value != null) {
fieldDescriptor.Accessor.SetValue(message, value);
}
}
}
}
private object? GenerateFieldValue(FieldDescriptor field) {
if (field.IsMap) {
return GenerateMapField(field);
}
// Handle oneof fields
if (field.ContainingOneof != null) {
// Randomly decide whether to set this oneof field
if (_random.Next(2) == 0) {
return null;
}
}
return GenerateSingleValue(field);
}
private object? GenerateSingleValue(FieldDescriptor field) {
if (field.FieldType == FieldType.Message && field.MessageType.FullName == "google.protobuf.Timestamp") {
return GenerateTimestamp();
}
return field.FieldType switch {
FieldType.Double => _random.NextDouble() * 1000,
FieldType.Float => (float)(_random.NextDouble() * 1000),
FieldType.Int64 or FieldType.SInt64 or FieldType.SFixed64 => (long)_random.Next(1, 1000),
FieldType.UInt64 or FieldType.Fixed64 => (ulong)_random.Next(1, 1000),
FieldType.Int32 or FieldType.SInt32 or FieldType.SFixed32 => _random.Next(1, 1000),
FieldType.Fixed32 or FieldType.UInt32 => (uint)_random.Next(1, 1000),
FieldType.Bool => _random.Next(2) == 1,
FieldType.String => GenerateRandomString(),
FieldType.Bytes => GenerateRandomBytes(),
FieldType.Enum => GenerateEnumValue(field.EnumType),
FieldType.Message => GenerateNestedMessage(field.MessageType),
_ => null,
};
}
private object? GenerateMapField(FieldDescriptor field) {
var count = _random.Next(1, _maxCollectionSize + 1);
var mapField = field.Accessor.GetValue((IMessage?)Activator.CreateInstance(field.ContainingType.ClrType));
var mapDescriptor = field.MessageType;
var keyDescriptor = mapDescriptor.FindFieldByNumber(1); // Key is always field 1
var valueDescriptor = mapDescriptor.FindFieldByNumber(2); // Value is always field 2
var addMethod = mapField.GetType().GetMethod("Add");
if (addMethod == null) { return null; }
for (int i = 0; i < count; i++) {
var key = GenerateSingleValue(keyDescriptor);
var value = GenerateSingleValue(valueDescriptor);
if (key != null && value != null) {
addMethod.Invoke(mapField, [key, value]);
}
}
return mapField;
}
private IMessage? GenerateNestedMessage(MessageDescriptor messageDescriptor) {
var message = (IMessage?)Activator.CreateInstance(messageDescriptor.ClrType);
if (message == null) { return null; }
PopulateMessage(message);
return message;
}
private object GenerateEnumValue(EnumDescriptor enumDescriptor) {
var values = enumDescriptor.Values;
var randomIndex = _random.Next(values.Count);
return values[randomIndex].Number;
}
private Timestamp GenerateTimestamp() {
var now = DateTime.UtcNow;
var daysOffset = _random.Next(-365 * 2, 365 * 2); // ±2 years from now
var hoursOffset = _random.Next(-24, 24);
var minutesOffset = _random.Next(-60, 60);
var randomTime = now
.AddDays(daysOffset)
.AddHours(hoursOffset)
.AddMinutes(minutesOffset);
return Timestamp.FromDateTime(randomTime);
}
private string GenerateRandomString() {
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var length = _random.Next(1, _stringMaxLength + 1);
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[_random.Next(s.Length)]).ToArray());
}
private ByteString GenerateRandomBytes() {
var length = _random.Next(1, _stringMaxLength + 1);
var bytes = new byte[length];
_random.NextBytes(bytes);
return ByteString.CopyFrom(bytes);
}
}
// Example usage:
/*
public class Program
{
public static void Main()
{
var faker = new ProtobufFaker();
// Generate fake data for any protobuf message
var message = faker.Generate<YourProtobufMessage>();
// The faker will properly handle:
// - All protobuf field types
// - Repeated fields
// - Map fields
// - Oneof fields
// - Nested messages
// - Enums
// - Optional fields
Console.WriteLine(message.ToString());
}
}
*/
https://gist.github.com/stasberkov/2d12925ea1d30c712fe9240bd5c7ea8b
Top comments (0)