如何将具有可变结构的消息展平为protobuf?

时间:2017-04-25 18:08:19

标签: parsing go protocol-buffers

我的proto buf格式是:

package main;

message Test {
  optional string id = 1;
  optional string name = 2;
  optional string age = 3;
}

然后我使用以下代码从golang中的输入填充protobuf文件。 str已经被解析了。

test = &Test{
  id: proto.String(str[0]),
  name:  proto.String(str[1]),
  age: proto.String(str[2]),
},

我必须处理的一个条件是测试结构中的一个或多个可选字段可能随机缺失,我事先并不知道。我如何处理golang?

为了提供更多上下文,真实数据在文件中可能如下所示:

id=1, name=peter, age=24
id=2, age=30
name=mary, age=31
id=100
name=bob
age=11

2 个答案:

答案 0 :(得分:1)

您可以使用正则表达式将输入字符串更改为有效的JSON,使用encoding/json包来解析它。这样做的好处是让json解析器可以为您处理所有事情。这是你特定情况的正则表达式。

基本上,正则表达式查找field=value并替换为"field" : "value"并将其包装在{}中以创建有效的JSON。逗号保留原样。

https://play.golang.org/p/_EEdTB6sve

package main

import (
    "encoding/json"
    "errors"
    "fmt"
    "log"
    "regexp"
)

var ins = []string{
    `id=1, name=peter, age=24`,
    `id=2, age=30`,
    `name=mary, age=31`,
    `id=100`,
    `name=bob`,
    `age=11`,
}

var ParseError = errors.New("bad parser input")
var Regex *regexp.Regexp

type Test struct {
    ID   string
    Name string
    Age  string
}

func (t *Test) String() string {
    return fmt.Sprintf("ID: %s, Name: %s, Age: %s", t.ID, t.Name, t.Age)
}

func main() {
    var err error
    Regex, err = regexp.Compile(`([^,\s]*)=([^,\s]*)`)
    if err != nil {
        log.Panic(err)
    }
    for _, v := range ins {

        test, err := ParseLine(v)
        if err != nil {
            log.Panic(err)
        }
        fmt.Println(test)
    }
}

func ParseLine(inp string) (*Test, error) {
    JSON := fmt.Sprintf("{%s}", Regex.ReplaceAllString(inp, `"$1" : "$2"`))
    test := &Test{}
    err := json.Unmarshal([]byte(JSON), test)
    if err != nil {
        return nil, err
    }
    return test, nil
}

我认为这是你所追求的最小工作案例,尽管我对协议缓冲区不够熟悉,无法正确打印字符串......或者甚至验证它们是否正确。请注意,这不会在操场上运行。

package main

import (
    "errors"
    "fmt"
    "log"
    "regexp"
    "github.com/golang/protobuf/jsonpb"
    _ "github.com/golang/protobuf/proto"
)

var ins = []string{
    `id=1, name=peter, age=24`,
    `id=2, age=30`,
    `name=mary, age=31`,
    `id=100`,
    `name=bob`,
    `age=11`,
}

var ParseError = errors.New("bad parser input")
var Regex *regexp.Regexp

type Test struct {
    ID   *string `protobuf:"bytes,1,opt,name=id,json=id" json:"id,omitempty"`
    Name *string `protobuf:"bytes,2,opt,name=name,json=name" json:"name,omitempty"`
    Age  *string `protobuf:"bytes,3,opt,name=age,json=age" json:"age,omitempty"`
}

func (t *Test) Reset() {
    *t = Test{}
}


func (*Test) ProtoMessage() {}
func (*Test) Descriptor() ([]byte, []int) {return []byte{}, []int{0}}

func (t *Test) String() string {
    return fmt.Sprintf("ID: %v, Name: %v, Age: %v", t.ID, t.Name, t.Age)
}

func main() {
    var err error
    Regex, err = regexp.Compile(`([^,\s]*)=([^,\s]*)`)
    if err != nil {
        log.Panic(err)
    }
    for _, v := range ins {

        test, err := ParseLine(v)
        if err != nil {
            fmt.Println(err)
            log.Panic(err)
        }
        fmt.Println(test)
    }
}

func ParseLine(inp string) (*Test, error) {
    JSON := fmt.Sprintf("{%s}", Regex.ReplaceAllString(inp, `"$1" : "$2"`))
    test := &Test{}
    err := jsonpb.UnmarshalString(JSON, test)
    if err != nil {
        return nil, err
    }
    return test, nil
}

答案 1 :(得分:0)

看起来您可以为输入的每一行编写解析器,如下所示。

注意:我没有使用proto值制作结构,因为作为外部包,它无法在操场上导入。

https://play.golang.org/p/hLZvbiMMlZ

package main

import (
    "errors"
    "fmt"
    "strings"
)

var ins = []string{
    `id=1, name=peter, age=24`,
    `id=2, age=30`,
    `name=mary, age=31`,
    `id=100`,
    `name=bob`,
    `age=11`,
}

var ParseError = errors.New("bad parser input")

type Test struct {
    ID   string
    Name string
    Age  string
}

func (t *Test) String() string {
    return fmt.Sprintf("ID: %s, Name: %s, Age: %s", t.ID, t.Name, t.Age)
}

func main() {
    for _, v := range ins {
        t, err := ParseLine(v)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Println(t)
        }
    }
}

func ParseLine(inp string) (*Test, error) {
    splt := strings.Split(inp, ",")
    test := &Test{}
    for _, f := range splt {
        fieldVal := strings.Split(strings.TrimSpace(f), "=")
        switch strings.TrimSpace(fieldVal[0]) {
        case "id":
            test.ID = strings.TrimSpace(fieldVal[1])
        case "name":
            test.Name = strings.TrimSpace(fieldVal[1])
        case "age":
            test.Age = strings.TrimSpace(fieldVal[1])
        default:
            return nil, ParseError
        }
    }
    return test, nil
}