4

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.

5

1 Answer 1

6

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

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, that works! I was already on the right path with the UnmarshalJSON method but had some problems assigning the value.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.