Reputation: 1445
I want to parse some JSON but one key is either a string or an object.
Here is my current struct: https://github.com/PhillippOhlandt/pmtoapib/blob/master/CollectionItemRequest.go#L10
type CollectionItemRequest struct {
Url string `json:"url"`
Method string `json:"method"`
Header []RequestHeader `json:"header"`
Body RequestBody `json:"body"`
Description string `json:"description"`
}
Here the "Url" attribute can not only a string but also an object.
I started to create an own struct for it that covers the object case.
type CollectionItemRequestUrl struct {
Raw string `json:"raw"`
}
type CollectionItemRequest struct {
Url CollectionItemRequestUrl `json:"url"`
Method string `json:"method"`
Header []RequestHeader `json:"header"`
Body RequestBody `json:"body"`
Description string `json:"description"`
}
But then the string version won't work anymore. Is there a way to have both cases working and getting the value via a getter, like request.Url.Get
?
EDIT:
Here are the two versions of the JSON:
"request": {
"url": {
"raw": "http://localhost:8081/users?per_page=5&page=2",
"protocol": "http",
"host": [
"localhost"
],
"port": "8081",
"path": [
"users"
],
"query": [
{
"key": "per_page",
"value": "5",
"equals": true,
"description": ""
},
{
"key": "page",
"value": "2",
"equals": true,
"description": ""
}
],
"variable": []
},
And
"request": {
"url": "http://localhost:8081/users/2",
Note: Only subsets, the whole JSON would be too long.
Upvotes: 4
Views: 1571
Reputation: 3394
Have a type that has a custom unmarshal method that will first unmarshal into an empty interface and then does a type switch on whether it got a string
or a map[string]interface{}
such as this:
type example struct {
URL myURL `json:"url"`
}
type myURL struct {
url string
}
func (u *myURL) MarshalJSON() ([]byte, error) {
return json.Marshal(u.url)
}
func (u *myURL) UnmarshalJSON(data []byte) error {
var raw interface{}
json.Unmarshal(data, &raw)
switch raw := raw.(type) {
case string:
*u = myURL{raw}
case map[string]interface{}:
*u = myURL{raw["raw"].(string)}
}
return nil
}
const myStringURL string = `{"url": "http://www.example.com/as-string"}`
const myNestedURL string = `{"url": {"raw": "http://www.example.com/as-nested"}}`
func main() {
var stringOutput example
json.Unmarshal([]byte(myStringURL), &stringOutput)
fmt.Println(stringOutput)
var nestedOutput example
json.Unmarshal([]byte(myNestedURL), &nestedOutput)
fmt.Println(nestedOutput)
}
Runnable in go playground here:
https://play.golang.org/p/I6KC4aXHpxm
Upvotes: 6