Home > Back-end >  Accept dynamic keys in proto struct when unmarshalling JSON
Accept dynamic keys in proto struct when unmarshalling JSON

Time:11-25

My Proto file looks something like this:

    message Test {
        Service services = 1;
    }
    
    message Service {
        string command = 1;
        string root = 2;
    }

This .proto can support a json like this:

    {
            "services": {   
                "command": "command2",
                "root": "/" 
            },
    }

But, I want to manage a json that looks like this:

       {
                "services": {
                        "service1": {
                            "command": "command1",
                            "root": "/"
                        },
                        "service2": {
                            "command": "command2",
                            "root": "/"
                        },
                },
        }

So, here all the services will have common structure but the key name will vary (i.e. "service1", "service2")

Now, I want to read the data from test.json and unmarshal it:

    var test *Test
    err := json.Unmarshal([]byte(file), &test)

What changes should I do in the .proto so that I can unmarshall this json successfully?

CodePudding user response:

Use a proto map:

message Test {
    map<string, Service> services = 1;
}

message Service {
    string command = 1;
    string root = 2;
}

The proto map is compiled to map[K]V in Go, so map[string]*Service in this case, which is the recommended way to model JSON with arbitrary keys.

This will give the following output:

services:{key:"service1" value:{command:"command1" root:"/"}} services:{key:"service2" value:{command:"command2" root:"/"}}

Example program:

package main

import (
    "encoding/json"
    "example.com/pb"
    "fmt"
)

const file = `{
            "services": {
                    "service1": {
                        "command": "command1",
                        "root": "/"
                    },
                    "service2": {
                        "command": "command2",
                        "root": "/"
                    }
            }
    }
`


func main() {
    test := &pb.Test{}
    err := json.Unmarshal([]byte(file), test)
    if err != nil {
        panic(err)
    }
    fmt.Println(test)
}
  • Related