How to deserialize JSON when a member's type changes

Viewed 63

I'm writing a go program to download my newspaper and convert it into ePub.

I get the articles as JSON and figured out the structure as below (excertpt)

type Article struct {
    Id         string    `json:"id"`
    Type       string    `json:"type"`
    Title      string    `json:"title"`
    Author     string    `json:"author"`
    Underline  string    `json:"underline"`
    Next       Link      `json:"next"`
    Prev       Link      `json:"prev"`
}
type Link struct {
    Id    string `json:"id"`
    Paper Paper  `json:"paper"`
}
type Paper struct {
    Paper string `json:"paper"`
    Date  string `json:"date"`
    Title string `json:"title"`
}

An example json:

{
    "id": "89052007",
    "type": "article",
    "title": "The Title",
    "author": "The Author",
    "next": {
        "id": "89094743",
        "paper": {
            "paper": "xxx",
            "date": 20200822,
            "title": "Newspaper"
        },
        "title": "Next Title"
    },
    "prev": {
        "id": "89094746",
        "paper": {
            "paper": "xxx",
            "date": 20200822,
            "title": "Newspaper"
        },
        "title": "Prev Title"
    }
}

The issue I have is with the Next/Prev link, when there is no previous or next article. In that case, Next/Prev will not be of type "Link" but of type "bool". The JSON will simply read:

…
    "prev": false
}

As a beginner of go I need some advice how to handle this situation properly.

Currently when I "hit" such an article, my program panics:

panic: assignment to entry in nil map

I read about How to make a nullable field in a struct as suggested but cannot see how this is going to help. I simply cannot relate it to my issue.

1 Answers

You can implement the json.Unmarshaler interface.

For example:

type Article struct {
    Id        string `json:"id"`
    Type      string `json:"type"`
    Title     string `json:"title"`
    Author    string `json:"author"`
    Underline string `json:"underline"`
    Next      Link   `json:"next"`
    Prev      Link   `json:"prev"`
}

type Link struct {
    Id    string `json:"id"`
    Paper Paper  `json:"paper"`
}

func (l *Link) UnmarshalJSON(data []byte) error {
    if string(data) == "false" {
        return nil
    }

    type link Link
    return json.Unmarshal(data, (*link)(l))
}

https://play.golang.org/p/fQhDWwO45Nt

Or:

type Article struct {
    Id        string       `json:"id"`
    Type      string       `json:"type"`
    Title     string       `json:"title"`
    Author    string       `json:"author"`
    Underline string       `json:"underline"`
    Next      OptionalLink `json:"next"`
    Prev      OptionalLink `json:"prev"`
}

type Link struct {
    Id    string `json:"id"`
    Paper Paper  `json:"paper"`
}

type OptionalLink struct {
    Link *Link
}

func (ol *OptionalLink) UnmarshalJSON(data []byte) error {
    if string(data) == "false" {
        return nil
    }

    ol.Link = new(Link)
    return json.Unmarshal(data, ol.Link)
}

https://play.golang.org/p/PMrbMJAOgaA

Related