ryan961 / memo

https://github.com/ryan961/memo/issues
Creative Commons Zero v1.0 Universal
0 stars 0 forks source link

Go JSON: How do you represent a JSON field in Go that could be absent, null or have a value? #4

Open ryan961 opened 7 months ago

ryan961 commented 7 months ago

🤖️ AI Summary

The article discusses handling JSON fields in Go that can be absent, null, or have a value. It presents a challenge when differentiating between fields that are unspecified or explicitly set to null. The solution involves a custom type Nullable[T] that uses a map to represent three states: field not set, set to null, or set to a value. This approach, avoiding the use of pointers, allows clear distinction between these states during JSON marshaling and unmarshaling.

🖇️ Details

📝 Note

// Code taken from https://github.com/oapi-codegen/nullable/blob/v1.0.0/nullable.go

// Nullable is a generic type, which implements a field that can be one of three states:
//
// - field is not set in the request
// - field is explicitly set to `null` in the request
// - field is explicitly set to a valid value in the request
//
// Nullable is intended to be used with JSON marshalling and unmarshalling.
//
// Internal implementation details:
//
// - map[true]T means a value was provided
// - map[false]T means an explicit null was provided
// - nil or zero map means the field was not provided
//
// If the field is expected to be optional, add the `omitempty` JSON tags. Do NOT use `*Nullable`!
//
// Adapted from https://github.com/golang/go/issues/64515#issuecomment-1841057182
type Nullable[T any] map[bool]T

// other fields and methods omitted

func (t Nullable[T]) MarshalJSON() ([]byte, error) {
    // if field was specified, and `null`, marshal it
    if t.IsNull() {
        return []byte("null"), nil
    }

    // if field was unspecified, and `omitempty` is set on the field's tags, `json.Marshal` will omit this field

    // otherwise: we have a value, so marshal it
    return json.Marshal(t[true])
}

func (t *Nullable[T]) UnmarshalJSON(data []byte) error {
    // if field is unspecified, UnmarshalJSON won't be called

    // if field is specified, and `null`
    if bytes.Equal(data, []byte("null")) {
        t.SetNull()
        return nil
    }
    // otherwise, we have an actual value, so parse it
    var v T
    if err := json.Unmarshal(data, &v); err != nil {
        return err
    }
    t.Set(v)
    return nil
}
ryan961 commented 7 months ago

📦 Package

github.com/oapi-codegen/nullable

🤖️ AI Intro

This package provides tools and code functionalities for managing nullable values during JSON marshaling and unmarshaling.. In situations where data might not have a definite value, this repository offers mechanisms to handle those values, reducing possibilities of errors or bugs in your project. By incorporating this repository into your work, you can have a consistent and strong handling of nullable values.

📝 Example

package main

import (
    "encoding/json"
    "fmt"

    "github.com/oapi-codegen/nullable"
)

func main() {
    obj := struct {
        Name nullable.Nullable[string] `json:"name"`
    }{}

    // when it's not set
    err := json.Unmarshal([]byte(`
        {
        }
        `), &obj)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    fmt.Println("Unspecified:")
    fmt.Printf("obj.Name.IsSpecified(): %v\n", obj.Name.IsSpecified())
    fmt.Printf("obj.Name.IsNull(): %v\n", obj.Name.IsNull())
    fmt.Println("---")
}

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
blackstorm commented 7 months ago

Hi Ryan961,

What AI summarization tool did you use for this article? Have you tried Gitblog's AI meta generator tool?

AI SEO Metadata Generator

ryan961 commented 7 months ago

@blackstorm hi~ I've implemented the information in the 🤖️ AI Summary section based on coze.Thanks for your provision of the Gitblog tool and the AI meta generator tool. I'm looking forward to trying them out later.😄

blackstorm commented 7 months ago

@blackstorm hi~ I've implemented the information in the 🤖️ AI Summary section based on coze.Thanks for your provision of the Gitblog tool and the AI meta generator tool. I'm looking forward to trying them out later.😄

Thanks Ryan!