首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >当我试图解析一个包含重复元素的字典结构时,我得到了一个错误"CMessage‘对象不适用于'str’对象“

当我试图解析一个包含重复元素的字典结构时,我得到了一个错误"CMessage‘对象不适用于'str’对象“
EN

Stack Overflow用户
提问于 2021-07-19 20:01:29
回答 1查看 142关注 0票数 0

你能帮助我理解为什么会崩溃吗:我正在尝试使用protobuf ParseDict函数将Python对象的结构转换为单个Protocol Buffer对象。

作为实验,我尝试制作一些protobuf实例,将它们转换为字典结构,然后再将它们转换回protobuf。

我写了以下代码:

代码语言:javascript
运行
复制
msg0=MsgGrpType.MsgType(subject="test_subject0")
msg1=MsgGrpType.MsgType(subject="test_subject1")
msg2=MsgGrpType.MsgType(subject="test_subject3")
msgGrp = MsgGrpType(flow="BOALF", msg=[msg0,msg1,msg2], pub_ts=datetime.datetime.now().isoformat())
msgDict = google.protobuf.json_format.MessageToDict(msgGrp)
result = ParseDict(msgDict, MsgGrpType, ignore_unknown_fields=True) # Crashes
pprint.pprint(result)

当我运行该脚本时,msgDict具有以下结构:

代码语言:javascript
运行
复制
{'flow': 'BOALF',
 'msg': [{'subject': 'test_subject0'},
         {'subject': 'test_subject1'},
         {'subject': 'test_subject3'}],
 'pubTs': '2021-07-19T13:18:40.263824'}

脚本崩溃,并显示以下错误:

代码语言:javascript
运行
复制
self = <google.protobuf.json_format._Parser object at 0x0000020177A3F2E0>
js = {'flow': 'BOALF', 'msg': [{'subject': 'test_subject0'}, {'subject': 'test_subject1'}, {'subject': 'test_subject3'}], 'pubTs': '2021-07-19T13:18:40.263824'}
message = <class 'com_elexeon_boalf_pb2.MsgGrpType'>

    def _ConvertFieldValuePair(self, js, message):
      """Convert field value pairs into regular message.
    
      Args:
        js: A JSON object to convert the field value pairs.
        message: A regular protocol message to record the data.
    
      Raises:
        ParseError: In case of problems converting.
      """
      names = []
      message_descriptor = message.DESCRIPTOR
      fields_by_json_name = dict((f.json_name, f)
                                 for f in message_descriptor.fields)
      for name in js:
        try:
          field = fields_by_json_name.get(name, None)
          if not field:
            field = message_descriptor.fields_by_name.get(name, None)
          if not field and _VALID_EXTENSION_NAME.match(name):
            if not message_descriptor.is_extendable:
              raise ParseError('Message type {0} does not have extensions'.format(
                  message_descriptor.full_name))
            identifier = name[1:-1]  # strip [] brackets
            # pylint: disable=protected-access
            field = message.Extensions._FindExtensionByName(identifier)
            # pylint: enable=protected-access
            if not field:
              # Try looking for extension by the message type name, dropping the
              # field name following the final . separator in full_name.
              identifier = '.'.join(identifier.split('.')[:-1])
              # pylint: disable=protected-access
              field = message.Extensions._FindExtensionByName(identifier)
              # pylint: enable=protected-access
          if not field:
            if self.ignore_unknown_fields:
              continue
            raise ParseError(
                ('Message type "{0}" has no field named "{1}".\n'
                 ' Available Fields(except extensions): {2}').format(
                     message_descriptor.full_name, name,
                     [f.json_name for f in message_descriptor.fields]))
          if name in names:
            raise ParseError('Message type "{0}" should not have multiple '
                             '"{1}" fields.'.format(
                                 message.DESCRIPTOR.full_name, name))
          names.append(name)
          value = js[name]
          # Check no other oneof field is parsed.
          if field.containing_oneof is not None and value is not None:
            oneof_name = field.containing_oneof.name
            if oneof_name in names:
              raise ParseError('Message type "{0}" should not have multiple '
                               '"{1}" oneof fields.'.format(
                                   message.DESCRIPTOR.full_name, oneof_name))
            names.append(oneof_name)
    
          if value is None:
            if (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE
                and field.message_type.full_name == 'google.protobuf.Value'):
              sub_message = getattr(message, field.name)
              sub_message.null_value = 0
            elif (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM
                  and field.enum_type.full_name == 'google.protobuf.NullValue'):
              setattr(message, field.name, 0)
            else:
              message.ClearField(field.name)
            continue
    
          # Parse field value.
          if _IsMapEntry(field):
            message.ClearField(field.name)
            self._ConvertMapFieldValue(value, message, field)
          elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
>           message.ClearField(field.name)
E           TypeError: descriptor 'ClearField' for 'google.protobuf.pyext._message.CMessage' objects doesn't apply to a 'str' object

C:\installs\anaconda\envs\eunrg_elexeon_protobufs\lib\site-packages\google\protobuf\json_format.py:561: TypeError

During handling of the above exception, another exception occurred:

    def test3():
        msg0=MsgGrpType.MsgType(subject="test_subject0")
        msg1=MsgGrpType.MsgType(subject="test_subject1")
        msg2=MsgGrpType.MsgType(subject="test_subject3")
        msgGrp = MsgGrpType(flow="BOALF", msg=[msg0,msg1,msg2], pub_ts=datetime.datetime.now().isoformat())
    
        msgDict = google.protobuf.json_format.MessageToDict(msgGrp)
        pprint.pprint(msgDict)
>       result = ParseDict(msgDict, MsgGrpType, ignore_unknown_fields=True) # Crashes

test_boalf.py:52: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
C:\installs\anaconda\envs\eunrg_elexeon_protobufs\lib\site-packages\google\protobuf\json_format.py:454: in ParseDict
    parser.ConvertMessage(js_dict, message)
C:\installs\anaconda\envs\eunrg_elexeon_protobufs\lib\site-packages\google\protobuf\json_format.py:485: in ConvertMessage
    self._ConvertFieldValuePair(value, message)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <google.protobuf.json_format._Parser object at 0x0000020177A3F2E0>
js = {'flow': 'BOALF', 'msg': [{'subject': 'test_subject0'}, {'subject': 'test_subject1'}, {'subject': 'test_subject3'}], 'pubTs': '2021-07-19T13:18:40.263824'}
message = <class 'com_elexeon_boalf_pb2.MsgGrpType'>

    def _ConvertFieldValuePair(self, js, message):
      """Convert field value pairs into regular message.
    
      Args:
        js: A JSON object to convert the field value pairs.
        message: A regular protocol message to record the data.
    
      Raises:
        ParseError: In case of problems converting.
      """
      names = []
      message_descriptor = message.DESCRIPTOR
      fields_by_json_name = dict((f.json_name, f)
                                 for f in message_descriptor.fields)
      for name in js:
        try:
          field = fields_by_json_name.get(name, None)
          if not field:
            field = message_descriptor.fields_by_name.get(name, None)
          if not field and _VALID_EXTENSION_NAME.match(name):
            if not message_descriptor.is_extendable:
              raise ParseError('Message type {0} does not have extensions'.format(
                  message_descriptor.full_name))
            identifier = name[1:-1]  # strip [] brackets
            # pylint: disable=protected-access
            field = message.Extensions._FindExtensionByName(identifier)
            # pylint: enable=protected-access
            if not field:
              # Try looking for extension by the message type name, dropping the
              # field name following the final . separator in full_name.
              identifier = '.'.join(identifier.split('.')[:-1])
              # pylint: disable=protected-access
              field = message.Extensions._FindExtensionByName(identifier)
              # pylint: enable=protected-access
          if not field:
            if self.ignore_unknown_fields:
              continue
            raise ParseError(
                ('Message type "{0}" has no field named "{1}".\n'
                 ' Available Fields(except extensions): {2}').format(
                     message_descriptor.full_name, name,
                     [f.json_name for f in message_descriptor.fields]))
          if name in names:
            raise ParseError('Message type "{0}" should not have multiple '
                             '"{1}" fields.'.format(
                                 message.DESCRIPTOR.full_name, name))
          names.append(name)
          value = js[name]
          # Check no other oneof field is parsed.
          if field.containing_oneof is not None and value is not None:
            oneof_name = field.containing_oneof.name
            if oneof_name in names:
              raise ParseError('Message type "{0}" should not have multiple '
                               '"{1}" oneof fields.'.format(
                                   message.DESCRIPTOR.full_name, oneof_name))
            names.append(oneof_name)
    
          if value is None:
            if (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE
                and field.message_type.full_name == 'google.protobuf.Value'):
              sub_message = getattr(message, field.name)
              sub_message.null_value = 0
            elif (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM
                  and field.enum_type.full_name == 'google.protobuf.NullValue'):
              setattr(message, field.name, 0)
            else:
              message.ClearField(field.name)
            continue
    
          # Parse field value.
          if _IsMapEntry(field):
            message.ClearField(field.name)
            self._ConvertMapFieldValue(value, message, field)
          elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
            message.ClearField(field.name)
            if not isinstance(value, list):
              raise ParseError('repeated field {0} must be in [] which is '
                               '{1}.'.format(name, value))
            if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
              # Repeated message field.
              for item in value:
                sub_message = getattr(message, field.name).add()
                # None is a null_value in Value.
                if (item is None and
                    sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'):
                  raise ParseError('null is not allowed to be used as an element'
                                   ' in a repeated field.')
                self.ConvertMessage(item, sub_message)
            else:
              # Repeated scalar field.
              for item in value:
                if item is None:
                  raise ParseError('null is not allowed to be used as an element'
                                   ' in a repeated field.')
                getattr(message, field.name).append(
                    _ConvertScalarFieldValue(item, field))
          elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
            if field.is_extension:
              sub_message = message.Extensions[field]
            else:
              sub_message = getattr(message, field.name)
            sub_message.SetInParent()
            self.ConvertMessage(value, sub_message)
          else:
            if field.is_extension:
              message.Extensions[field] = _ConvertScalarFieldValue(value, field)
            else:
              setattr(message, field.name, _ConvertScalarFieldValue(value, field))
        except ParseError as e:
          if field and field.containing_oneof is None:
            raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
          else:
            raise ParseError(str(e))
        except ValueError as e:
          raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
        except TypeError as e:
>         raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
E         google.protobuf.json_format.ParseError: Failed to parse msg field: descriptor 'ClearField' for 'google.protobuf.pyext._message.CMessage' objects doesn't apply to a 'str' object.

这是我的.proto文件:

代码语言:javascript
运行
复制
// com_elexeon_boalf.proto at 0:0
syntax = "proto3";
package com.elexeon.boalf;

message MsgGrpType {
  string pub_ts = 1;
  string flow = 2;
  repeated MsgType msg = 3;

  message MsgType {
    string subject = 1;
    int32 n_k = 2;
    string s_o = 3;
    string p_f = 4;
    string t_a = 5;
    string a_d = 6;
    int32 n_p = 7;
    repeated RowType row = 8;

    message RowType {
      string t_s = 1;
      double v_a = 2;
    }
  }
}
EN

回答 1

Stack Overflow用户

发布于 2021-08-08 00:17:51

google.protobuf.json_format.ParseDict函数将合并结果的消息作为其第二个参数。然后,它返回该消息。在您的示例中,您为它指定了一个类型(MsgGrpType)。如果希望将结果合并到frew new ("empty")消息中,则需要将MsgGrpType更改为MsgGrpType(),以便将类的一个新实例传递给ParseDict

(不幸的是,错误消息相当模糊。)

下面的Python片段似乎可以按预期工作:

代码语言:javascript
运行
复制
from com_elexeon_boalf_pb2 import MsgGrpType
import datetime
from google.protobuf.json_format import MessageToDict, ParseDict
import pprint

msg0=MsgGrpType.MsgType(subject="test_subject0")
msg1=MsgGrpType.MsgType(subject="test_subject1")
msg2=MsgGrpType.MsgType(subject="test_subject3")
msgGrp = MsgGrpType(flow="BOALF", msg=[msg0,msg1,msg2], pub_ts=datetime.datetime.now().isoformat())

msgDict = MessageToDict(msgGrp)
result = ParseDict(msgDict, MsgGrpType(), ignore_unknown_fields=True)

pprint.pprint(result)

执行时生成以下输出:

代码语言:javascript
运行
复制
pub_ts: "2021-08-08T01:15:44.232683"
flow: "BOALF"
msg {
  subject: "test_subject0"
}
msg {
  subject: "test_subject1"
}
msg {
  subject: "test_subject3"
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68440156

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档