Home  >  Article  >  Backend Development  >  Golang Unmarshal with different set of structural tags

Golang Unmarshal with different set of structural tags

WBOY
WBOYforward
2024-02-09 23:10:09526browse

具有不同结构标签集的 Golang Unmarshal

php editor Baicao today introduces to you a powerful Golang feature - Unmarshal with different structural tag sets. In Golang programming, Unmarshal is a process of converting data into a structure. However, when our data source contains different sets of structural tags, the traditional Unmarshal method may not meet the needs. Therefore, we need to use Golang Unmarshal with a different set of structural tags to achieve this functionality. This article will introduce in detail the usage and precautions of this feature. Let’s explore together!

Question content

I am using a third party tool's api which has custom key names in its json. I also have to use the api on two different environments (production and staging). Unfortunately, the custom fields in the api have different key names in both environments to represent the same data. In the example below, the json key custom-1 in the production environment is exactly the same as the json key custom-7 in the staging environment. I want to unmarshal any of them into the same data structure but I don't know how. I was hoping there was a way to somehow override the json.unmarshal() function for tags that use json on prod, but use jsonstaging# when staging ##. To me, this makes the most sense and is the simplest solution. I guess I'll have to write a custom unmarshaljson(data []byte) error function for my jsonobj type, but again, I don't know how to achieve what I want in a custom function the behavior of. Can anyone point me in the right direction, some documentation, or some examples 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 it with go run I get

production:  { id: "object-a", desc: "test" }
staging:  { id: "", desc: "" }

This is what my current code is expecting, but I would like to get

Production:  { Id: "object-a", Desc: "test" }
Staging:  { Id: "object-a", Desc: "test" }

I cannot modify the api of the staging or production environment.

I've tried creating different structures and interfaces, but as the number of fields (and custom json keys) increases (and they grow) it seems to be a maintenance nightmare. Please help me if this is the only way, I didn't get it to work either before I decided this might not be the right path.

Workaround

For future reference, if anyone wants 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 the

map[string] interface{} instead of the object you want to build.

Then I wrote a function to get the environment and map. It loops through all the fields in the new instance of the actual object (not the map) and gets the label for the environment you're using. It then sets the field in the new object to

objmap[tag].(f215d538f93896d2fa3ea4137b420524). After setting all the fields with labels it will return the new object.

This 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 above is the detailed content of Golang Unmarshal with different set of structural tags. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete