How can I work with SQL NULL values and JSON in a good way?

SqlJsonGo

Sql Problem Overview


Go types like Int64 and String cannot store null values, so I found I could use sql.NullInt64 and sql.NullString for this.

But when I use these in a Struct, and generate JSON from the Struct with the json package, then the format is different to when I use regular Int64 and String types.

The JSON has an additional level because the sql.Null*** is also a Struct.

Is there a good workaround for this, or should I not use NULLs in my SQL database?

Sql Solutions


Solution 1 - Sql

Types like sql.NullInt64 do not implement any special handling for JSON marshaling or unmarshaling, so the default rules apply. Since the type is a struct, it gets marshalled as an object with its fields as attributes.

One way to work around this is to create your own type that implements the json.Marshaller / json.Unmarshaler interfaces. By embedding the sql.NullInt64 type, we get the SQL methods for free. Something like this:

type JsonNullInt64 struct {
	sql.NullInt64
}

func (v JsonNullInt64) MarshalJSON() ([]byte, error) {
    if v.Valid {
		return json.Marshal(v.Int64)
	} else {
		return json.Marshal(nil)
	}
}

func (v *JsonNullInt64) UnmarshalJSON(data []byte) error {
	// Unmarshalling into a pointer will let us detect null
	var x *int64
	if err := json.Unmarshal(data, &x); err != nil {
		return err
	}
	if x != nil {
		v.Valid = true
		v.Int64 = *x
	} else {
		v.Valid = false
	}
	return nil
}

If you use this type in place of sql.NullInt64, it should be encoded as you expect.

You can test this example here: http://play.golang.org/p/zFESxLcd-c

Solution 2 - Sql

If you use the null.v3 package, you won't need to implement any of the marshal or unmarshal methods. It's a superset of the sql.Null structs and is probably what you want.

package main

import "gopkg.in/guregu/null.v3"

type Person struct {
	Name     string      `json:"id"`
	Age      int         `json:"age"`
	NickName null.String `json:"nickname"` // Optional
}

If you'd like to see a full Golang webserver that uses sqlite, nulls, and json you can consult this gist.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlexView Question on Stackoverflow
Solution 1 - SqlJames HenstridgeView Answer on Stackoverflow
Solution 2 - SqlStephen WoodView Answer on Stackoverflow