88

Since empty string is the zero/default value for Go string, I decided to define all such fields as interface{} instead. for example

type student struct {
    FirstName  interface{} `json:"first_name"`
    MiddleName interface{} `json:"middle_name"`
    LastName   interface{} `json:"last_name"`
}

The application I am sending my data expect a null instead of an empty string if value is not available for that specific field.

Is this the correct approach or can someone please point me to something better than this.

2
  • 6
    How about a pointer: *string? Also related: How do I represent an Optional String in Go? Commented Jun 25, 2015 at 11:07
  • @AryehArmon could you provide a link to the docs where that tag is specified? I can't seem to find it. Commented Jun 25, 2015 at 11:22

5 Answers 5

164
+500

In json package documentation :

Pointer values encode as the value pointed to. A nil pointer encodes as the null JSON object.

So you can store a pointer to a string which will be encoded as a string if not nil and will be encoded as "null" if nil

type student struct {
  FirstName  *string `json:"first_name"`
  MiddleName *string `json:"middle_name"`
  LastName   *string `json:"last_name"`
}
Sign up to request clarification or add additional context in comments.

4 Comments

I did tried this approach. Whether this approach and the interface one has any advantage over each other?
I would say readability and consistency. It is clear that with the pointer to string approach you know what the var is about. Using an interface tells the compiler that FirstName can be anything and leaves room for errors
Proposed approach does not work with other types like numbers. Any recommendations?
@stanley_manley This approach works nicely for me with float64.
20

For the case of a json object with null strings, its easiest to use the omitempty decorator on the field.

type student struct {
  FirstName  string `json:"first_name,omitempty"`
  MiddleName string `json:"middle_name,omitempty"`
  LastName   string `json:"last_name"`
}

With the above declaration, only if first_name was assigned will that key show up in the resultant json. last_name, on the otherhand, will always show up in the result with a value of "" if not assigned.

Now when you start including numeric fields where 0 could be a value, using omitempty doesn't do what one would expect. A 0 value always drops the field, and we need to be able to differentiate between a 0 value and an unassigned value. Here use of library such as https://github.com/guregu/null may be advisable.

More discussion here: https://www.sohamkamani.com/blog/golang/2018-07-19-golang-omitempty/

2 Comments

The question asks for setting null instead of the zero value, not to remove the field from the JSON response. The suggested solution, which seems to bring additional obstacles, does not answer the question.
Default in Go is to omit null values, which is fairy standard. I can understand if the type is read by another language that chooses to make a distinction between an omitted field and a null field. If the other languages does something different when the value is null vs omitted, I would say that is a bug they should fix. Legacy systems have their quirks though and you can't always fix them.
11

Can be used https://github.com/guregu/null

type student struct {
FirstName  null.String `json:"first_name"`
MiddleName null.String `json:"middle_name"`
LastName   null.String `json:"last_name"`}

3 Comments

@Dragonthoughts offsite resources? He's linking to a project on Github, which is a highly appropriate thing to do.
@Rob_vH What happens when that particular github repo is deleted, or made private? The answer loses significant content.
True, but if the github repo goes away, the go library goes away with it
9

Another way actually is a workaround with using the MarhshalJSON and UnmarshalJSON interface method offered by the json lib of golang. The code is as below:

type MyType string
type MyStruct struct {
    A MyType `json:"my_type"`
}

func (c MyType) MarshalJSON() ([]byte, error) {
    var buf bytes.Buffer
    if len(string(c)) == 0 {
        buf.WriteString(`null`)
    } else {
        buf.WriteString(`"` + string(c) + `"`)   // add double quation mark as json format required
    }
    return buf.Bytes(), nil
}

func (c *MyType)UnmarshalJSON(in []byte) error {
    str := string(in)
    if str == `null` {
        *c = ""
        return nil
    }
    res := MyType(str)
    if len(res) >= 2 {
        res = res[1:len(res)-1]     // remove the wrapped qutation
    }
    *c = res
    return nil
}

then when using json.Marshal, the MyType value will be marshaled as null.

Comments

8

You could use something like sql.NullString,use 'Valid' to check if it is a nil value.

NullString represents a string that may be null. NullString implements the Scanner interface so it can be used as a scan destination:

type NullString struct {
    String string
    Valid  bool // Valid is true if String is not NULL
}

var s NullString
err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s)
...
if s.Valid {
   // use s.String
} else {
   // NULL value
}

Please DO NOT use pointers like below:

type student struct {
  FirstName  *string `json:"first_name"`
  MiddleName *string `json:"middle_name"`
  LastName   *string `json:"last_name"`
}

Because you need check nil value and dereference like below everywhere,and maybe cause unexpected crash somewhere:

if obj.FirstName != nil {
   fmt.Print("%s", *obj.FirstName)
}

I have compared two solution and choose former method in my extensive production code... it works ok.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.