doufuhao8085
doufuhao8085
2014-12-25 00:32

根据参数值在Golang中解码传入的JSON

已采纳

I am trying to decode an incoming JSON in my REST API written in Go. I am using decoder.Decode() function and my problem is that I need to apply a certain rules on which struct should be used in the process of decoding because sometimes the JSON contains:

"type": {
    "type" : "string",
    "maxLength" : 30
},

and sometimes:

"type": {
    "type" : "integer",
    "max" : 30,
    "min" : 10
},

I somehow need to tell Go that "If the type.type is string, use this struct (type Type_String struct) and if the type.type is integer, use other struct (type Type_Integer struct)". I am not really sure how to do it. One solution which is on my mind is to make an universal struct with the all possible properties, use it on any kind of object and then filter the properties based on the type property but this is just so dirty. I guess I can also write my own decoder but that seems also a bit strange.

I am new to Go and I am pretty much used to the freedom JavaScript offers.

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

2条回答

  • doutinghou6980 doutinghou6980 7年前

    You can always decode to interface{} like mentioned here: How to access interface fields on json decode?

    http://play.golang.org/p/3z8-unhsH4

    package main
    
    import (
        "encoding/json"
        "fmt"
    )
    
    var one string = `{"type": {"type": "string", "maxLength":30}}`
    var two string = `{"type": {"type": "integer", "max":30, "min":10}}`
    
    func f(data map[string]interface{}) {
        t := data["type"]
        typemap := t.(map[string]interface{})
        t2 := typemap["type"].(string)
        switch t2 {
        case "string":
            fmt.Println("maxlength:", typemap["maxLength"].(float64))
        case "integer":
    
            fmt.Println("max:", typemap["max"].(float64))
        default:
            panic("oh no!")
        }
    }
    
    func main() {
        var jsonR map[string]interface{}
        err := json.Unmarshal([]byte(one), &jsonR)
        if err != nil {
            panic(err)
        }
        f(jsonR)
        json.Unmarshal([]byte(two), &jsonR)
        f(jsonR)
    }
    

    The idea is to unmarshal to map[string]interface{} and then cast and compare before accessing values.

    In the above code, the f function does the cast and compare. Given this poor json, I used poor variable name, t and t2 to represent the json values of "type" at different depths. Once t2 has the value, the switch statement does something with the "string" or the "integer" and what it does is print the maxLength or the max value.

    点赞 评论 复制链接分享
  • dth2331 dth2331 7年前

    First of all, if fields of "type" depends on "type.type", in my opinion, it's better to move it one level up. Something like:

    ...
    "type" : "integer",
    "intOptions": {
        "max" : 30,
        "min" : 10
    },
    ....
    

    Then you can create a struct with only one field:

    type Type struct {
        Type string
    }
    

    and do something like:

    myType := new(Type)
    json.Unmarshal([]byte(yourJsonString), myType)
    

    And now, depending on myType's value you can use different structs for decoding your json.

    点赞 评论 复制链接分享