I am working with an API for a third party tool and it contains custom key names in it's JSON. I also have to work with the API on two different environments (prod and staging). Unfortunately, the custom fields in the API have different key names on the two environments to represent the same data. In the example below, the json key custom-1
on production is exactly equivalent to the json key custom-7
on staging. I want to unmarshal either one into the same data structure, but I do not know how. I am hoping there is a way to somehow override the tag that the json.Unmarshal()
function uses to use json
on Prod, but use jsonStaging
on staging. To me, that is the solution that makes the most sense and would be simplest. I'm guessing I have to write a custom UnmarshalJSON(data []byte) error
function for my jsonObj
type, but again, I don't know how to achieve the desired behavior in the custom function. Can someone point me in the right direction, to some documentation, or to some examples that I can use?
package main
import (
"encoding/json"
"fmt"
)
type jsonObj struct {
Id string `json:"custom-1" jsonStaging:"custom-7"`
Desc string `json:"custom-2" jsonStaging:"custom-8"`
}
func (i jsonObj) String() string {
return fmt.Sprintf(`{ Id: "%s", Desc: "%s" }`, i.Id, i.Desc)
}
func main() {
var jsonProd = `{
"custom-1": "object-a",
"custom-2": "test"
}
`
var jsonStaging = `{
"custom-7": "object-a",
"custom-8": "test"
}
`
var jsonObjProd jsonObj
var jsonObjStaging jsonObj
json.Unmarshal([]byte(jsonProd), &jsonObjProd)
json.Unmarshal([]byte(jsonStaging), &jsonObjStaging)
fmt.Println("Production: ", jsonObjProd)
fmt.Println("Staging: ", jsonObjStaging)
}
When I run this with go run, I get
Production: { Id: "object-a", Desc: "test" }
Staging: { Id: "", Desc: "" }
That is expected with my current code, but I would like to get
Production: { Id: "object-a", Desc: "test" }
Staging: { Id: "object-a", Desc: "test" }
I do not have the ability to modify the API for either staging or production environments.
I have tried creating different structs and interfaces, but this seems like a maintenance nightmare as the number of fields (and therefore custom json keys) increase (they will). If that is the only way, please help me with that as well as I did not get that to work either before I decided that it was probably not the correct path.
For future reference if anyone is looking to do this, I think I found a way using the built-in reflect
package.
First, you have to use the json.Unmarshal() function, but populate a map[string]interface{}
instead of the object you want to build.
I then wrote a function that takes the environment and the map. It goes through all of the fields in a new instance of your actual object (not the map) and gets the tag for the environment you're using. Then it sets the field in the new object to objMap[tag].(<variable_type>)
. Once all of the fields are set using their tag, it returns the new object.
Here is my working code:
package main
import (
"encoding/json"
"fmt"
"reflect"
)
const (
StagingStructTag = "jsonStaging"
ProductionStructTag = "json"
)
type jsonObj struct {
Id string `json:"custom-1" jsonStaging:"custom-7"`
Desc string `json:"custom-2" jsonStaging:"custom-8"`
}
func (i jsonObj) String() string {
return fmt.Sprintf(`{ Id: "%s", Desc: "%s" }`, i.Id, i.Desc)
}
func main() {
var jsonProd = `{
"custom-1": "object-a",
"custom-2": "test"
}
`
var jsonStaging = `{
"custom-7": "object-a",
"custom-8": "test"
}
`
var env string = "staging"
var jsonObjProd jsonObj
var jsonObjStaging jsonObj
var jsonObjProdMap map[string]interface{}
var jsonObjStagingMap map[string]interface{}
json.Unmarshal([]byte(jsonStaging), &jsonObjStagingMap)
json.Unmarshal([]byte(jsonProd), &jsonObjProdMap)
jsonObjStaging = BuildJsonObj(env, jsonObjStagingMap)
env = "production"
jsonObjProd = BuildJsonObj(env, jsonObjProdMap)
fmt.Println("Production: ", jsonObjProd)
fmt.Println("Staging: ", jsonObjStaging)
}
func BuildJsonObj(env string, objMap map[string]interface{}) jsonObj {
var obj jsonObj
var t reflect.Type = reflect.TypeOf(obj)
var structTagName string
if env == "staging" {
structTagName = StagingStructTag
} else if env == "production" {
structTagName = ProductionStructTag
}
for i := 0; i < t.NumField(); i++ {
var field reflect.StructField = t.Field(i)
var tag string
var ok bool
if tag, ok = field.Tag.Lookup(structTagName); ok {
switch field.Name {
case "Id":
obj.Id = objMap[tag].(string)
case "Desc":
obj.Desc = objMap[tag].(string)
}
}
}
return obj
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.