Unmarshal JSON NULL в указатель NullString - PullRequest
0 голосов
/ 01 октября 2018

Я не могу json.Unmarshal пустое значение в поле *NullString в структуре.Вот упрощенный пример того, что я имею в виду:

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}`)
}

Как видите, демаршаллинг работает для ненулевых значений json.Но когда я передаю нулевое значение json, unmarshaller NullString, похоже, даже не запускается.

Кто-нибудь знает, что я делаю неправильно?

Справочная информация

Причина, по которой я пытаюсь это сделать, заключается в том, что я планирую получить значение JSON из REST API.Не все поля в API являются обязательными полями.Поэтому я использую указатели для своих структурных полей, чтобы помочь мне построить оператор SQL Update, потому что:

  • поле с нулем означает, что оно не заполнено (не включают SET name = ?)
  • не-nil NullString.Valid == false означает фактическое нулевое значение (включая SET name = NULL)
  • и ненулевое NullString.Valid == true означает, что существует действительное строковое значение (включая SET name = ?)

1 Ответ

0 голосов
/ 01 октября 2018

Да, это из-за следующего правила демаршалинга:

Чтобы демонтировать JSON в указатель, Unmarshal сначала обрабатывает случай, когда JSON является литералом JSON, равным нулю.В этом случае Unmarshal устанавливает указатель на ноль.В противном случае Unmarshal отменяет маршализацию JSON в значение, на которое указывает указатель.

( Документация для encoding/json).

Я предлагаю сделать следующее:добавить поле Set, которое изменяется на true при запуске UnmarshalJSON (которое при наличии какого-либо значения гарантированно будет запущено), а затем изменить *NullString на простой NullString, например, так:

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}`)
}

Детская площадка

...