How do I access field values of nested messages in C# ?

546 views
Skip to first unread message

Oskar Emil Skeide

unread,
Feb 5, 2020, 8:55:06 AM2/5/20
to Protocol Buffers
For compatibility reasons I am writing a custom serializer for a generic protobuf message.

I a using Google.Protobuf 3.11.3 from NuGet and used protoc to generate my C# classes.

Given the proto file

message Foo {
    optional
string other = 1;
 
   optional Bar bar = 2;
}

message
Bar {
    optional
string hello = 1;
}


I have a serialize method
        public static string Serialize(IMessage message)
       
{
           
if (message == null)
           
{
               
throw new ArgumentNullException(nameof(message));
           
}

           
           
var result = WriteFields(message, message.Descriptor.Fields, 0);
           
return result.ToString();
       
}

and WriteFields
        private static StringBuilder WriteFields(IMessage message, MessageDescriptor.FieldCollection fields, int depth)
       
{
           
var sb = new StringBuilder();
           
foreach (var field in fields.InFieldNumberOrder())
           
{
               
if (field.Accessor.HasValue(message))
               
{
                   
if (field.FieldType == FieldType.Message)
                   
{
                        sb
.Append($"{field.Name} {{\n");
                        sb
.Append(WriteFields(message, field.MessageType.Fields, depth + 1));
                        sb
.Append("}\n");
                   
}
                   
else
                   
{
                        sb
.Append($"{field.Name}: {field.Accessor.GetValue(message)}\n");
                   
}
               
}
           
}

           
return sb;
       
}

The first pass of the foreach-loop runs fine. Foo.other is written as expected.

The second pass fails, at this point I know the field has FieldType.Message and I call WriteFields recursively to write the fields from Bar.

I get an InvalidCastException at field.Accessor.HasValue(message)now. 

System.InvalidCastException : Unable to cast object of type 'Test.Foo' to type 'Test.Bar'.
   at
Google.Protobuf.Reflection.ReflectionUtil.ReflectionHelper`2.<>c__DisplayClass4_0.<CreateFuncIMessageBool>b__0(IMessage message) in T:\src\github\protobuf\csharp\src\Google.Protobuf\Reflection\ReflectionUtil.cs:line 204
   at Test.Parser.WriteFields(IMessage message, FieldCollection fields, Int32 depth) in C:\src\Project\Parser.cs:line 85


I need to either
A) Convert the FieldDescriptor Bar to IMessage Bar
or
B) Find some other way to get the value than passing IMessage down the chain

I am not able to find out how to to this.



Reply all
Reply to author
Forward
0 new messages