-2

How can I unmarshal a JSON response where a empty field value returns as a string from server and other times returns as object?

{
"replies": "" // empty field from server
}

Server with some replies


    {
    "replies": {
    "author" : "fooname"
    }
    }

golang unmarshaling error as cannot convert string to type Replies

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
  • 1
    You can implement the unmarshaler interface on your Replies type and have it unmarshal anything you like. – mkopriva Jan 02 '20 at 12:37
  • I couldnt find a way to implement UnmarshalJSON () method for replies as it is deep down in json data provided by server. Could you provide an example for this. I was able to find https://gist.github.com/tkrajina/aec8d1b15b088c20f0df4afcd5f0c511 this gist so far, but they provided a way to unmarshal top level json – John McLane Jan 02 '20 at 12:50
  • 1
    You can implement `UnmarshalJSON` on any subset (struct) of a larger struct – xarantolus Jan 02 '20 at 13:56
  • @Adrian both question have different use cases. Its not duplicate. The question you suggested have use case of getting reaponse of distinct type. In here replies contains an object. – John McLane Jan 02 '20 at 15:44
  • @JohnMcLane it's not precisely identical but the differences are irrelevant to the problem and the solution is the same. – Adrian Jan 02 '20 at 15:46

2 Answers2

3

As mentioned in the comments, you need to implement the json.Unmarshaler interface to handle this case.

Assuming we start with these structs, we can see that the field that needs custom logic is of type Replies:

type Response struct {
    Replies Replies `json:"replies"`
}

type Replies struct {
    *realResp
}

// Contains actual data
type realResp struct {
    Author string `json:"author"`
}

Now we can implement the UnmarshalJSON method:

func (r *Replies) UnmarshalJSON(b []byte) error {
    if string(b) == "\"\"" {
        return nil // Do nothing; you might also want to return an error
    }
    r.realResp = &realResp{} // Initialize r.realResp
    return json.Unmarshal(b, r.realResp)
}

Note the pointer receiver so UnmarshalJSON can modify r.

You can also see this full example.

xarantolus
  • 1,961
  • 1
  • 11
  • 19
  • Thanks I was able to get through this error but there is another problem with your solution as I couldn't unmarshal lets say suppose array of Author. Can you help me with this ?? – John McLane Jan 04 '20 at 09:50
  • You can replace `realResp` with whatever type you want to. You could use `[]realResp` instead of `*realResp` in the struct definition, which would then be an array/slice of these elements. If this doesn't help you can post the JSON you want to parse – xarantolus Jan 04 '20 at 10:52
-2

As golang is strictly typed, replies cannot have 2 types (string and object)

replies should return an object or null if there is none. This code might be helpful to understand.

package main

import (
    "encoding/json"
    "fmt"
)

type replies struct {
    Author string `json:"author"`
}

type resp struct {
    Replies *replies `json:"replies"`
}

func main() {
    obj := new(resp)
    response := []byte(`{"replies": {"author":"fooname"}}`)
    //response := []byte(`{"replies": null}`)

    err := json.Unmarshal(response, obj)
    if err != nil {
        fmt.Println(err)
    }

    if obj.Replies != nil {
        fmt.Println(obj.Replies.Author)
    } else {
        fmt.Println("Empty replies")
    }
}
Rohit Subedi
  • 560
  • 3
  • 13