I have the following Go struct into which I want to unmarshal some Json data. It works perfectly except for the Values
map, which is of type map[string]string
.
type Data struct {
Id int `jons:"id"`
Values map[string]string `json:"values"`
My Json data (which I can't change the format of), has the following structure and sample data:
{
id: 1,
values: {
key1: "a string value",
key2: 7
}
}
Unmarshalling the json data fails because Go can't unmarshal the value 7
into a string.
json: cannot unmarshal number into Go struct field Data.Values of type string
Is there a way to implicitly convert the Json values into a string, regardless of the perceived type?
Changing the Json data to format the value as a string, ie key2: "7"
is not an option.
CodePudding user response:
Since you can have an integer or a string in the json, it would be better if you use an interface.
Something like this:
type Data struct {
Id int `jons:"id"`
Values map[string]interface{} `json:"values"`
}
This should do the trick.
Sample code for reference: https://play.golang.org/p/PjxWeLTwsCC
CodePudding user response:
You can create own string type and implement UnmarshalJSON function to it.
type MadSrting string
type Data struct {
Id int `jons:"id"`
Values map[string]MadSrting `json:"values"`
}
func (mad *MadSrting) UnmarshalJSON(data []byte) error {
if n := len(data); n > 1 && data[0] == '"' && data[n-1] == '"' {
return json.Unmarshal(data, (*string)(mad))
}
*mad = MadSrting(data)
return nil
}
Sample: https://play.golang.org/p/PsJRsvQJPMZ
CodePudding user response:
You can use interface and type assertions
package main import ( "encoding/json" "fmt" "strconv" ) func main() { type Data struct { Id int `jons:"id"` Values map[string]interface{} `json:"values"` } jsonData := []byte(`{"id": 1, "values": {"key1": "a string value", "key2": 7}}`) data := new(Data) err := json.Unmarshal(jsonData, &data) if err != nil { fmt.Println(err) } for _, value := range data.Values { fmt.Printf("%T\n", ToString(value)) } } func ToString(value interface{}) string { str := "" switch value.(type) { case float64: str = strconv.FormatFloat(value.(float64), 'f', 0, 64) case int64: str = strconv.FormatInt(value.(int64), 10) case int: str = strconv.Itoa(value.(int)) case string: str = value.(string) } return str }
https://play.golang.org/p/r9_6IKRgPst
CodePudding user response:
You can use the empty interface on your maps values. An then convert it to a string with Sprintf. Here is an example of how you could unmarshal your data:
package main
import (
"encoding/json"
"fmt"
)
type Data struct {
Id int `jons:"id"`
Values map[string]interface{} `json:"values"`
}
func main() {
data := `
{
"id": 1,
"values": {
"key1": "a string value",
"key2": 7
}
}
`
var d Data
err := json.Unmarshal([]byte(data), &d)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(d)
s1 := fmt.Sprintf("%v", d.Values["key1"])
fmt.Println("key1", s1)
s2 := fmt.Sprintf("%v", d.Values["key2"])
fmt.Println("key2", s2)
}