Home > Backend Development > Golang > How Can I Unmarshal Unknown Protobuf Messages in Go?

How Can I Unmarshal Unknown Protobuf Messages in Go?

Barbara Streisand
Release: 2024-11-30 09:21:13
Original
934 people have browsed it

How Can I Unmarshal Unknown Protobuf Messages in Go?

Pitfalls of Unmarshaling Unknown Protobuf Messages

The inability of proto.Unmarshal to handle interface{} types is an essential aspect to understand. Its method signature stipulates passing a proto.Message argument, which is implemented by concrete protobuffer types.

Overcoming the Challenge

When dealing with raw protobuffer payloads that lack additional context, it's crucial to have at least some identifying information (e.g., a string or number) alongside the byte slice. This information can be used to map to the specific protobuffer concrete message. You can then use a switch statement to instantiate the appropriate type and pass it to Unmarshal:

switch atLeastSomething {
    case "foo":
        message = &mypb.Foo{}
    case "bar":
        message = &mypb.Bar{}
}
_ = proto.Unmarshal(data, message)
Copy after login

Embracing the Unpredictable: Decoding Completely Unknown Messages

In rare instances, you may encounter a completely unknown protobuffer payload. The protowire package provides a solution to extract some information from it, albeit with inherent limitations. The payload content can be retrieved, but with weaker semantics.

Implementation Details

Here's a simplified parser for unknown proto messages:

func parseUnknown(b []byte) []Field {
    // Data model
    type Field struct {
        Tag Tag
        Val Val
    }
    type Tag struct {
        Num int32
        Type protowire.Type
    }
    type Val struct {
        Payload interface{}
        Length  int
    }
    // Parsing algorithm
    fields := make([]Field, 0)
    for len(b) > 0 {
        n, t, fieldlen := protowire.ConsumeField(b)
        if fieldlen < 1 {
            return nil
        }
        ... // Parsing logic
        fields = append(fields, field)
        b = b[fieldlen:]
    }
    return fields
}
Copy after login

Sample Input and Output

With the sample input:

message Foo {
  string a = 1;
  string b = 2;
  Bar bar = 3;
}
message Bar {
  string c = 1;
}
&test.Foo{A: "A", B: "B", Bar: &test.Bar{C: "C"}}
Copy after login

The output will be:

Field{Tag:Tag{Num:1, Type:2}, Val:Val{Payload:[]uint8{0x41}, Length:1}}
Field{Tag:Tag{Num:2, Type:2}, Val:Val{Payload:[]uint8{0x42}, Length:1}}
Field{Tag:Tag{Num:1, Type:2}, Val:Val{Payload:[]uint8{0x43}, Length:1}}
Field{Tag:Tag{Num:3, Type:2}, Val:Val{Payload:[]Field{Field{Tag:Tag{Num:1, Type:2}, Val:Val{Payload:[]uint8{0x43}, Length:1}}}, Length:3}}
Copy after login

Additional Considerations

  • Sub-messages are recursively parsed and stored, if valid. Otherwise, bytes are stored as-is.
  • Repeated fields are not explicitly handled, but can be inferred from repeated Tag.Num values post-parsing.
  • Maps are expected to be semantically equivalent to repeated key/value pairs.
  • Oneof information is likely lost, preserving only the set value.

Alternative: Any and Interface{}

While Any might initially seem like a suitable option for unknown messages, it has a strictly defined structure and cannot be used for decoding unknown payloads. Furthermore, protowire is not applicable to Any due to its specific structure.

The above is the detailed content of How Can I Unmarshal Unknown Protobuf Messages in Go?. For more information, please follow other related articles on the PHP Chinese website!

source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Articles by Author
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template