0

We're using proto3 and trying to write a proto message to generate a golang structure which can be marshaled as a JSON output with a particular structure.

The data needs to have nested arrays of mixed types (specifically, the vCard format as specified here)

The problem we're running into is generating an array with mixed types. For example, just "vcardArray:["vcard",100] that is, an array with a string and an int32

If we use Oneof as so:

message Vcard {
    oneof vcard {
        string name = 1;
        int32 value = 2;
    }
}

We can generate something like:

[
{"name":"vcard"},
{"int":100}
]

If we use Any as so:

message VcardArray {
  repeated google.protobuf.Any vcard = 1;
}

We can generate:

[
    {
        type: "type.googleapis.com/google.protobuf.StringValue",
        value: "vcard"
    },
    {
        type: "type.googleapis.com/google.protobuf.Int32Value",
        value: 100
    },
]

We expect the next JSON:

 "vcardArray": [
    "vcard",
    [
        [ "version", {},"text","4.0"],
        [ "tel", {
            "pref":"1",
            "type":[
                "work",
                "voice"
                ]
            }
        ],
        [...],
        [...]
    ]
]

The central question, is it possible to generate arrays of of mixed elements such as ["vcard",[...]] and if so, how can it be done?

cristbait
  • 1
  • 1
  • Unfortunately in the way you are wanting to marshal it, the type information is lost. A proto-unmarshaller could no longer successfully determine what to unmarshal into without that. So it's not possible. What you could do is put all fields into a single type. Then it will only marshal these with values. But of course that's not very clean – Michael Ernst Aug 30 '19 at 08:01

1 Answers1

0

There's two parts of your question:

  1. What go struct if any would allow you generate the json with mixed type arrays?
  2. How might you write proto3 to have such a go struct generated?

The answer to your first question is that you can use an array of interface{}, which means basically an array of untyped values.

type Person struct {
    First   string        `json:"first_name"`
    Aliases []interface{} `json:"aliases"`
}

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

Now, knowing what the go struct you want is, how can you specify it in proto?

The short answer is you can't. (If you're curious, you can take a look at how the *.pb.go file is generated)

However, you could write a function that takes the struct that protoc will generate for you and translate it into your own struct. That means you'll have to keep it up to date as you need to add or remove fields. This also means that protoc can continue to use it's structs independently then how you might need to generate json.

Liyan Chang
  • 7,721
  • 3
  • 39
  • 59