在运行时确定结构时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:-
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
}
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.