将json null解组为NullString的指针

I am unable to json.Unmarshal a null value into a *NullString field within a struct. Here is a simplified example of what I mean:

package main

import (
  "database/sql"
  "encoding/json"
  "log"
)

// NullString
type NullString struct {
  sql.NullString
}

func (n *NullString) UnmarshalJSON(b []byte) error {

  n.Valid = string(b) != "null"
  e := json.Unmarshal(b, &n.String)
  return e
}

type Person struct {
  Name *NullString `json:"name"`
}

func BuildUpdateSQL(jsonString string) string {

  p := Person{}
  e := json.Unmarshal([]byte(jsonString),&p)
  if e != nil {
    log.Println(e)
  }

  if p.Name != nil {
    log.Println(p,p.Name)
  } else {
    log.Println(p)
  }
  return ""
}
func main() {
  // Correctly leaves p.Name unset
  BuildUpdateSQL(`{"field_not_exist":"samantha"}`)

  // Correctly sets p.Name
  BuildUpdateSQL(`{"name":"samantha"}`)

  // Incorrectly leaves p.Name as nil when I really want p.Name to have a NullString with .Valid == false
  BuildUpdateSQL(`{"name":null}`)
}

As you can see, unmarshalling works for non-null json values. But when I pass in a null json value, the NullString unmarshaller doesn't seem to even fire.

Anyone know what I'm doing wrong?

Background

The reason I'm trying to do this is because I plan to get JSON value from a REST API. Not all fields in the API are required fields. Hence I use pointers for my struct fields to help me build my SQL Update statement because:

  • field with nil means not populated (do not include a SET name = ?)
  • non-nil NullString.Valid == false means actual null value (include a SET name = NULL)
  • and non-nil NullString.Valid == true means a real string value exists (include a SET name = ?)

Yes, this is because of the following unmarshaling rule:

To unmarshal JSON into a pointer, Unmarshal first handles the case of the JSON being the JSON literal null. In that case, Unmarshal sets the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into the value pointed at by the pointer.

(Documentation for encoding/json).

What I suggest to do is to add a Set field which is changed to true when UnmarshalJSON is fired (which if there is any value, is guaranteed to be fired), and then to change the *NullString to a simple NullString, like so:

package main

import (
    "database/sql"
    "encoding/json"
    "log"
)

// NullString
type NullString struct {
    Set bool
    sql.NullString
}

func (n *NullString) UnmarshalJSON(b []byte) error {
    n.Set = true
    n.Valid = string(b) != "null"
    e := json.Unmarshal(b, &n.String)
    return e
}

type Person struct {
    Name NullString `json:"name"`
}

func BuildUpdateSQL(jsonString string) string {
    p := Person{}
    e := json.Unmarshal([]byte(jsonString), &p)
    if e != nil {
        log.Println(e)
    }

    log.Printf("%#v", p)
    return ""
}

func main() {
    BuildUpdateSQL(`{"field_not_exist":"samantha"}`)
    BuildUpdateSQL(`{"name":"samantha"}`)
    BuildUpdateSQL(`{"name":null}`)
}

Playground