如何使用jsonpb将JSON解组到包含其中一个定义的protobuf中?

时间:2018-07-04 13:27:09

标签: json go protocol-buffers

我目前无法解组由 jsonpb 生成的JSON代码段。也许这只是我的一种误解,但是在查看测试时,我希望它会以某种方式起作用。

以下是pb.proto的相关代码段:

syntax = "proto3";
package pb;

message Parameter {
    string name = 1;
    oneof value {
        string str_value = 2;
        int32 int_value = 3;
        bool bool_value = 4;
        float float_value = 5;
    }
}

message ParameterSet {
    bytes raw = 1;
    repeated Parameter parameters = 2;
}

message ParameterSets {
    map<string,ParameterSet> sets = 1;
}

使用此简单代码段测试封送/拆组失败:

package main

import (
    "fmt"
    "github.com/gogo/protobuf/jsonpb"
    "strings"
)

func main() {
    m := jsonpb.Marshaler{}
    str, err := m.MarshalToString(&pb.ParameterSets{Sets: map[string]*pb.ParameterSet{
        "parameter": {
            Raw: []byte{0, 1, 2, 3, 4, 5, 6, 1, 2},
            Parameters: []*pb.Parameter{
                {Name: "itest", Value: &pb.Parameter_IntValue{42}},
                {Name: "stest", Value: &pb.Parameter_StrValue{"Foobar"}},
                {Name: "btest", Value: &pb.Parameter_BoolValue{true}},
                {Name: "ftest", Value: &pb.Parameter_FloatValue{41.99}},
           },
        },
    }},)
    fmt.Println(str)
    fmt.Println(err)

    sets := pb.ParameterSets{}
    err = jsonpb.Unmarshal(strings.NewReader(str), &sets)
    fmt.Println(sets)
    fmt.Println(err)
}

结果为:

{"sets":{"parameter":{"raw":"AAECAwQFBgEC","parameters":[{"name":"itest","intValue":42},{"name":"stest","strValue":"Foobar"},{"name":"btest","boolValue":true},{"name":"ftest","floatValue":41.99}]}}}
<nil>
{map[]}
unknown field "intValue" in pb.Parameter

如何将 oneof 值重新返回到原始对象中?

2 个答案:

答案 0 :(得分:0)

由于两个供应商文件夹,您有一个Type混淆。在两个供应商文件夹中声明的类型不相同,即使它们的代码完全相同

因此例如: prototest-master/vendor/github.com/gogo/proto/messageSet(A)与prototest-master/ proto /vendor/github.com/gogo/proto/messageSet(B)的类型不同,即使它们的导入相同(github.com/gogo/proto

在项目的main.go中,封送封送程序jsonpb.Marshaler{}的构造使用(A)中的类型,而实际消息&pb.ParameterSets{...这样的消息则使用(B)中的类型来构造自身。 / p>

由于jsonpb似乎做了很多反射工作,因此在将两种类型混合使用时会中断。

一个更好的解决方案是仅使用一个vendor文件夹来明确每个人使用的类型。我只会放弃(B)供应商文件夹,因为它没有添加任何内容。

答案 1 :(得分:0)

我认为手动构建消息并不安全,因为您无法保证结果JSON与jsonpb.Marshal产生的结果完全匹配。最好只实例化ParameterThis github issue讨论了一种与您的情况类似的方案,one of the solutions可能会为您提供帮助:

package main

import (
    "fmt"
    "strings"

    "github.com/golang/protobuf/jsonpb"
    "github.com/golang/protobuf/proto"
)

func main() {
    msg := &Person{
        Name: "Allen",
        Age: 30,
        Married: true,
        Number: &Person_F{float32(14500.6)},
    }

    m := jsonpb.Marshaler{}
    js, err := m.MarshalToString(msg)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("json output ==> %v\n", js)

    msg2 := &Person{}
    if err := jsonpb.Unmarshal(strings.NewReader(js), msg2); err != nil {
        fmt.Println(err)
        return
    }

    fmt.Printf("%+v\n", proto.MarshalTextString(msg2))
}