【问题标题】:Iterate over unknown repeated protobuf field with FieldDescriptor使用 FieldDescriptor 迭代未知的重复 protobuf 字段
【发布时间】:2021-05-01 02:35:43
【问题描述】:

我正在尝试编写一个通用方法来遍历 protobuf 消息并基本上创建一个对象树。

目前为止

IMessage message = new Message() as IMessage;
foreach (FieldDescriptor field in message.Descriptor.Fields.InFieldNumberOrder())
            {
                if (field.FieldType == Google.Protobuf.Reflection.FieldType.Message)
                {
                    if (field.IsRepeated)
                    {
                        // How to do this?
                    }
                    else
                    {
                        Children.Add(new MessageNode(field, (IMessage)field.Accessor.GetValue(message)));
                    }
                }
                else
                {
                    Children.Add(new FieldNode(field, message));
                }
           }

除了重复字段外工作正常(地图字段可能会出现同样的问题)。

如果我对重复字段执行field.Accessor.GetValue(message),我会得到一个object[RepeatedField<T>],其中T 是一些嵌套消息。但是,我不能迭代 object 类型的东西。 我尝试将对象转换为 RepeatedField<IMessage> 但这只是返回 0。

有没有办法在不知道里面的类型的情况下迭代一个重复的字段?我只需要一个指向内部对象的IMessage 指针,从那里我可以使用IMessage 中的Descriptor

【问题讨论】:

    标签: c# iterator protocol-buffers


    【解决方案1】:

    我不知道在过去的两天里我试图解决这个问题是多么盲目......

    就在 GetValue 的文档中,Accessor

    对于重复值,这将是一个 IList 实现。对于地图值,这将是一个 IDictionary 实现。

    是的,IList d = field.Accessor.GetValue(message) as IList; 做到了这一点,并提供了重复字段的可迭代集合。

    【讨论】:

      【解决方案2】:

      正如您在your answer 中所述,根据the protobuf documentation field.Accessor.GetValue(protobufMessage) 将为重复字段返回IList,为映射字段返回IDictionary

      这是我编写的一些代码,它使用字段描述符递归地遍历 protobuf 消息。它可能是进行其他操作而不是打印的有用起点。

          public static void PrintProtobufMessage(IMessage protobufMessage)
          {
              if (protobufMessage == null)
              {
                  return;
              }
              foreach (var field in protobufMessage.Descriptor.Fields.InFieldNumberOrder())
              {
                  if (field.IsMap)
                  {
                      // for map fields, the field type is always a MapField, inside of which are contained a keyField and valueField
                      //var keyField = field.MessageType.Fields.InFieldNumberOrder()[0];
                      var valueField = field.MessageType.Fields.InFieldNumberOrder()[1];
                      IDictionary mapFields = field.Accessor.GetValue(protobufMessage) as IDictionary;
                      if (mapFields != null)
                      {
                          foreach (DictionaryEntry mapField in mapFields)
                          {
                              Console.WriteLine("Iterating Map, Field {0} ({1}): Key {2}:", field.FieldNumber, field.Name, mapField.Key);
                              if (valueField.FieldType == Google.Protobuf.Reflection.FieldType.Message)
                              {
                                  PrintProtobufMessage(mapField.Value as IMessage);
                              }
                              else
                              {
                                  PrintProtobufField(valueField, mapField.Value);
                              }
                          }
                      }
                  }
                  else if (field.IsRepeated)
                  {
                      IList repeatedFieldValues = field.Accessor.GetValue(protobufMessage) as IList;
                      if (repeatedFieldValues != null)
                      {
                          foreach (var repeatedFieldValue in repeatedFieldValues)
                          {
                              // for repeated fields, the field type represents each element in the list, handle them accordingly
                              if (field.FieldType == Google.Protobuf.Reflection.FieldType.Message)
                              {
                                  PrintProtobufMessage(repeatedFieldValue as IMessage);
                              }
                              else
                              {
                                  PrintProtobufField(field, repeatedFieldValue);
                              }
                          }
                      }
                  }
                  else
                  {
                      if (field.FieldType == Google.Protobuf.Reflection.FieldType.Message)
                      {
                          var fieldValue = field.Accessor.GetValue(protobufMessage);
                          PrintProtobufMessage(fieldValue as IMessage);
                      }
                      else
                      {
                          PrintProtobufField(field, protobufMessage);
                      }
                  }
              }
          }
      
          public static void PrintProtobufField(FieldDescriptor field, IMessage protobufMessage)
          {                   
              var fieldValue = field.Accessor.GetValue(protobufMessage);
              PrintProtobufField(field, fieldValue);
          }
      
          public static void PrintProtobufField(FieldDescriptor field, object fieldValue)
          {
              if (fieldValue != null)
              {
                  Console.WriteLine(
                      "Field {0} ({1}): {2}",
                      field.FieldNumber,
                      field.Name,
                      fieldValue
                  );
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多