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 vue3加ant-design-vue无法渲染出页面
  • ¥15 matlab(相关搜索:紧聚焦)
  • ¥15 基于51单片机的厨房煤气泄露检测报警系统设计
  • ¥15 路易威登官网 里边的参数逆向
  • ¥15 Arduino无法同时连接多个hx711模块,如何解决?
  • ¥50 需求一个up主付费课程
  • ¥20 模型在y分布之外的数据上预测能力不好如何解决
  • ¥15 processing提取音乐节奏
  • ¥15 gg加速器加速游戏时,提示不是x86架构
  • ¥15 python按要求编写程序