duanrong3308 2018-10-01 17:56
浏览 60
已采纳

将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 = ?)
  • 写回答

1条回答 默认 最新

  • duanliaozhi2915 2018-10-01 18:27
    关注

    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

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 delta降尺度计算的一些细节,有偿
  • ¥15 Arduino红外遥控代码有问题
  • ¥15 数值计算离散正交多项式
  • ¥30 数值计算均差系数编程
  • ¥15 redis-full-check比较 两个集群的数据出错
  • ¥15 Matlab编程问题
  • ¥15 训练的多模态特征融合模型准确度很低怎么办
  • ¥15 kylin启动报错log4j类冲突
  • ¥15 超声波模块测距控制点灯,灯的闪烁很不稳定,经过调试发现测的距离偏大
  • ¥15 import arcpy出现importing _arcgisscripting 找不到相关程序