在运行时确定结构时JSON Unmarshal

在运行时确定结构时JSON Unmarshal

问题描述:

Usually we Unmarshal a json object in Go as:

I am kind of a noob in Go, so pardon me if some syntax seems incorrect below.

type Item struct {
    Quantity    int     `json:"quantity,omitempty"`
    Price       float64 `json:"price,omitempty"`
    Currency    string  `json:"currency,omitempty"`
}
output := &Item{}
err:= json.Unmarshal([]byte(data), output)

Now the catch is my json could be different at runtime depending on some field. Price could be string, array with different value or json containing currency and price in one object.

I have this mapping in the db, how could I write my code so that I would read that mapping of column name to type and then unmarshall it creating the suitable struct at runtime. For e.g. I need to unmarshall following JSON's in same code:-

{"Quantity": 5, "Price": 64.5, "Currency": "USD"}
{"Quantity": 5, "Purchase": {"Price": 64.5, "Currency": "USD"}}

I would already have mapping like Quantity -> int, Purchase -> JSON String for second json with me in the DB.

tl;dr

Need to unmarshall json where structure changes at runtime on the basis of some parameters and I know the structure beforhand

Edit: Rephrase

I need function which will return me the object of above struct take json string and json format string as input.

CustomUnmarshal([]byte(data) []byte, format string) (*item){}

I have written a sample code here:-

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

If your output structure and the keys in the input remain the same then it is possible to do what you require using the Unmarshaler interface.

type Unmarshaler interface {
        UnmarshalJSON([]byte) error
}

I've implemented a very crude, string only struct implimentation.

type Item struct {
    Quantity string `json:"quantity,omitempty"`
    Price    string `json:"price,omitempty"`
    Currency string `json:"currency,omitempty"`
}

Like I said it's very crude with a lot of assumptions and no checks in place.

func (itm *Item) UnmarshalJSON(byt []byte) (err error) {
    jsn := string(byt)
    jsn = strings.Replace(jsn,"{","",-1)
    jsn = strings.Replace(jsn,"}","",-1)

    jsnArr := strings.Split(jsn,":")
    fmt.Println(jsnArr)

    for i, val := range jsnArr {
        switch {
        case strings.Contains(val,"Quantity"):
            itm.Quantity = strings.Split(jsnArr[i+1],",")[0]
        case strings.Contains(val,"Price"):
            itm.Price = strings.Split(jsnArr[i+1],",")[0]
        case strings.Contains(val,"Currency"):
            itm.Currency = strings.Split(jsnArr[i+1],",")[0]    
        }
    }
    return
}

Full Program

Also note that the json tags you set for the structure represents how the json input keys should be, so keys like "Quantity" should be "quantity" in the JSON input, and so on.