Golang空值以及结构体omitempty的应用

1. 零值、空值、空结构体

零值

golang在声明一个变量时,每个类型都会有一个默认零值,例如:

  • 对于值类型:布尔类型为 false, 数值类型为 0,字符串为"",数组和结构会递归初始化其元素或字段,即其初始值取决于元素或字段。
  • 对于引用类型: 均为 nil,包括指针 pointer,函数 function,接口 interface,切片 slice,管道 channel,映射 map。

因此在声明一个变量绑定数据类型后,没有设置其初始值时,该变量都会有一个默认值,为该类型的零值

空值 nil

nil 是 Golang 中预先声明的标识符(非关键字保留字),其主要用来表示引用类型的零值(指针,接口,函数,映射,切片和通道),表示它们未初始化的值fagn

// [src/builtin/builtin.go](https://golang.org/src/builtin/builtin.go#L98)
//
// nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.
var nil Type // Type must be a pointer, channel, func, interface, map, or slice type

nil 是 Golang 中唯一没有默认类型的非类型化的值,它不是一个未定义的状态。所以你不能像这样使用它:

a := nil
// cannot declare variable as untyped nil: a零值切片	var zeroSlice = make([]int, 0)
	fmt.Println(zeroSlice)        // []
	fmt.Println(zeroSlice == nil) // false
	fmt.Println(len(zeroSlice))   // 0
	fmt.Println(cap(zeroSlice))   // 0nil切片	var nilSlice []int
	fmt.Println(nilSlice)        // []
	fmt.Println(nilSlice == nil) // true
	fmt.Println(len(nilSlice))   // 0
	fmt.Println(cap(nilSlice))   // 0
nil切片是一个未初始化的切片变量,而零值切片已经初始化了,只是内部没有任何元素

空结构体

空结构是没有任何字段的结构类型,例如:

type Q struct{}
var q struct{}

iface和eface

iface 和 eface 都是 Go 中描述接口的底层结构体,区别在于 iface 描述的接口包含方法,而 eface 则是不包含任何方法的空接口:interface{}。

iface

iface的源码如下

type iface struct {
     
     
	tab  *itab
	data unsafe.Pointer
}

type itab struct {
     
     
	inter  *interfacetype
	_type  *_type
	link   *itab
	hash   uint32 // copy of _type.hash. Used for type switches.
	bad    bool   // type does not implement interface
	inhash bool   // has this itab been added to hash?
	unused [2]byte
	fun    [1]uintptr // variable sized
}

iface内部维护了两个指针:

tab指向一个itab实例,它表示接口的类型以及赋给这个接口的实体类型

data 则指向接口具体的值,一般而言是一个指向堆内存的指针

在itab结构体中:

_type 字段描述了实体的类型,包括内存对齐方式,大小等

inter 字段则描述了接口的类型

fun 字段放置和接口方法对应的具体数据类型的方法地址,实现接口调用方法的动态分派,一般在每次给接口赋值发生转换时会更新此表,或者直接拿缓存的 itab。fun的数组大小为1,这里存储的是第一个方法的函数指针,一个接口一般会有多个方法,会在它之后的内存空间里面存储

interfacetype 类型,它描述的是接口的类型:

type interfacetype struct {
     
     
	typ     _type
	pkgpath name
	mhdr    []imethod
}

_type :实际上是描述 Go 语言中各种数据类型的结构体

mhdr 字段:表示接口所定义的函数列表

pkgpath: 记录定义了接口的包名

eface

eface的源码如下:

type eface struct {
     
     
    _type *_type
    data  unsafe.Pointer
}

相比 iface,eface 就比较简单了。只维护了一个 _type 字段,表示空接口所承载的具体的实体类型。data 描述了具体的值。

结构体omitempty的应用

定义结构体 CAPIBaseResponse

type CAPIBaseResponse struct {
     
     
	Code      int    `json:"Code"`
	Status    string `json:"Status"`
	Msg       string `json:"Msg"`
	RequestId string `json:"RequestId"`
}

给该结构体赋一个空值func main() {
     
     
	data := ""
	capi := new(CAPIBaseResponse)
	json.Unmarshal([]byte(data), &capi)
	addressBytes, _ := json.MarshalIndent(capi, "", "    ")
	fmt.Printf("%s\n", string(addressBytes))
}
打印结果:{
     
     
    "Code": 0,
    "Status": "",
    "Msg": "",
    "RequestId": ""
}
修改结构体,给字段加上omitemptytype CAPIBaseResponse struct {
     
     
	Code      int    `json:"Code,omitempty"`
	Status    string `json:"Status,omitempty"`
	Msg       string `json:"Msg"`
	RequestId string `json:"RequestId"`
}
打印结果:{
     
     
    "Msg": "",
    "RequestId": ""
}当添加上omitempty属性后,如果赋的是字段类型的默认空值,会隐藏该字段

修改结构体:

type CAPIBaseResponse struct {
     
     
	Code      int    `json:"Code,omitempty"`
	Status    string `json:"Status,omitempty"`
	Msg       string `json:"Msg"`
	RequestId string `json:"RequestId"`
   Coordinate coordinate `json:"coordinate,omitempty"`
}
type coordinate struct {
     
     
	Lat float64 `json:"latitude"`
	Lng float64 `json:"longitude"`
}
继续赋空值,打印结果{
     
     
    "Msg": "",
    "RequestId": "",
    "coordinate": {
     
     
        "latitude": 0,
        "longitude": 0
    }
}
可以发现,给coordinate添加上omitempty,但是其结构体字段没添加,依旧会显示。

继续修改结构体

type CAPIBaseResponse struct {
     
     
	Code       int        `json:"Code,omitempty"`
	Status     string     `json:"Status,omitempty"`
	Msg        string     `json:"Msg"`
	RequestId  string     `json:"RequestId"`
	Coordinate coordinate `json:"coordinate,omitempty"`
}

type coordinate struct {
     
     
	Lat float64 `json:"latitude,omitempty"`
	Lng float64 `json:"longitude,omitempty"`
}

打印结果:{
     
     
    "Msg": "",
    "RequestId": "",
    "coordinate": {}
}
给其结构体的每个字段都加上omitempty,依旧会显示"coordinate":{},因为golang不知道这个结构体的“空值”

继续修改结构体

type CAPIBaseResponse struct {
     
     
	Code       int        `json:"Code,omitempty"`
	Status     string     `json:"Status,omitempty"`
	Msg        string     `json:"Msg"`
	RequestId  string     `json:"RequestId"`
	Coordinate *coordinate `json:"coordinate,omitempty"`
}

type coordinate struct {
     
     
	Lat float64 `json:"latitude"`
	Lng float64 `json:"longitude"`
}
打印结果:{
     
     
    "Msg": "",
    "RequestId": ""
}
将Coordinate定义为指针类型后,golang就知道一个指针的“空值”是多少,因此隐藏了Coordinate,否则golang是不知道我们定义的结构体的”空值“。

将结构体修改为

type CAPIBaseResponse struct {
     
     
	Code       int        `json:"Code,omitempty"`
	Status     string     `json:"Status,omitempty"`
	Msg        string     `json:"Msg"`
	RequestId  string     `json:"RequestId"`
	Coordinate *coordinate `json:"coordinate,omitempty"`
}

type coordinate struct {
     
     
	Lat float64 `json:"latitude,omitempty"`
	Lng float64 `json:"longitude,omitempty"`
}

修改赋值代码,如果给field赋的值正好是该类型的默认空值时func main() {
     
     
	data := `{
     
     
	    "coordinate": {
     
     
			"latitude": 0.0,
		   "longitude": 0.0
		}
	}`
	capi := new(CAPIBaseResponse)
	json.Unmarshal([]byte(data), &capi)
	addressBytes, _ := json.MarshalIndent(capi, "", "    ")
	fmt.Printf("%s\n", string(addressBytes))
}
打印结果:{
     
     
    "Msg": "",
    "RequestId": "",
    "coordinate": {}
}
结构体的字段被隐藏了,因为赋的值刚好为float64类型的默认空值,但是依旧会显示"coordinate": {},因为不是nil。

继续修改结构体

type CAPIBaseResponse struct {
     
     
	Code       int         `json:"Code,omitempty"`
	Status     string      `json:"Status,omitempty"`
	Msg        string      `json:"Msg"`
	RequestId  string      `json:"RequestId"`
	Coordinate *coordinate `json:"coordinate,omitempty"`
}

type coordinate struct {
     
     
	Lat *float64 `json:"latitude,omitempty"`
	Lng *float64 `json:"longitude,omitempty"`
}

打印结果:{
     
     
    "Msg": "",
    "RequestId": "",
    "coordinate": {
     
     
        "latitude": 0,
        "longitude": 0
    }
}
将Lat和Lng改为指针类型后,即使赋的是float64的默认空值,也会正常显示,因为*float64的默认空值为nil

修改赋值

func main() {
     
     
	data := ""
	capi := new(CAPIBaseResponse)
	json.Unmarshal([]byte(data), &capi)
	addressBytes, _ := json.MarshalIndent(capi, "", "    ")
	fmt.Printf("%s\n", string(addressBytes))
}

打印结果:{
     
     
    "Msg": "",
    "RequestId": ""
}
因为赋的是nil,是指针的空值,所以被隐藏了。

总结

  • 在判断变量是否为nil时,需要先弄清变量的类型,对于值类型,声明后都会有一个默认零值,因此不会为nil,对于引用类型,需要先判断其是否初始化,只有当类型和值同时为空时,才为nil(比如 var nilSlice []int 就是一个nil切片,而 var zeroSlice = make([]int, 0) 就不是nil切片)
  • iface和eface都是 Go 中描述接口的底层结构体,区别在于iface描述的接口包含方法,而eface描述的接口不包含方法;
  • 在结构体的json中使用omitempty时,一定要清楚定义的类型的默认空值,只有当其为默认空值时才会省略,比如int类型,当值为0时就会省略,如果改成*int,当值为nil时才会省略,为0依旧会正常显示;对于子结构体,非引用类型类型,golang并不知道其“空值”,即使没有赋值也会显示空结构体,修改为引用类型后,其空值就为nil,在没有赋值的时候就不会显示了。

<think>好的,我现在需要帮助用户解决如何在Excel VBA中解析JSON的问题。根据用户提供的引用内容,我需要整理出几种可行的方法,并提供代码示例。首先,用户提到的引用中提到了几种不同的方法,比如使用VBA-JSON库、利用HTMLFile对象执行JScript,以及通过自定义函数处理JSON对象。我需要把这些方法逐一分析,确保步骤清晰,代码正确。 首先,方法一应该是使用VBA-JSON解析器库。根据引用[2]和引用[1],用户需要下载JsonConverter.bas文件,并导入到VBA项目中。同时,可能还需要添加对Microsoft Scripting Runtime的引用。这里需要注意用户可能对如何导入文件不熟悉,需要详细说明步骤,比如在VBA编辑器中选择“文件”->“导入文件”,然后选择下载好的JsonConverter.bas。然后给出示例代码,展示如何解析JSON字符串和文件。比如,使用JsonConverter.ParseJson方法,将字符串转换为字典或集合对象。同时,处理文件时需要用Open语句读取内容,再进行解析。这部分需要确保代码正确,语法无误。 接下来是方法二,使用HTMLFile对象。根据引用[3]和引用[4],这个方法利用JScript的解析能力。用户需要创建一个HTMLFile对象,然后通过执行JScript代码将JSON字符串转换为对象。这里需要注意CreateObject("HTMLFile")的用法,以及如何通过.parentWindow和execScript来执行脚本。示例代码中的JsonParse函数应该能够将JSON字符串转换为对象,然后通过GetProperty函数获取属性值。这部分可能需要说明如何遍历对象,或者处理嵌套结构,但用户提供的示例比较简单,可能需要补充更多细节。 然后是方法三,处理简单结构的手动解析。如果JSON结构比较简单,用户可以使用Split和Replace函数来提取数据。不过这种方法只适用于非常简单的JSON,比如数组中的对象,没有嵌套的情况。例如引用[5]中的JSON示例,可以通过分割字符串来获取各个字段的值。但这种方法容易出错,特别是当JSON格式复杂或有转义字符时,所以需要提醒用户谨慎使用。 在整理这些方法时,需要注意LaTeX格式的正确应用,比如行内公式用$...$,独立公式用$$...$$,但在这个问题中可能涉及代码较多,数学公式较少。不过用户提到的系统级指令要求所有行内数学表达式使用正确格式,所以需要检查是否有数学内容需要处理,比如变量名中的下划线或特殊符号可能需要用LaTeX转义。 另外,用户要求生成相关问题,我需要根据解析JSON的方法,提出一些相关问题,比如如何安装VBA-JSON库、处理嵌套JSON结构、优化解析性能等。这些问题应该覆盖用户可能遇到的后续疑问,帮助用户进一步深入学习。 最后,确保回答结构清晰,分步骤说明每种方法,代码示例正确,并且引用标识正确添加到对应的内容后面。比如在提到VBA-JSON库时,引用[1]和[2],使用HTMLFile对象时引用[3]和[4],手动解析时引用[5]。同时,检查是否有遗漏的引用或错误链接。 可能需要注意的细节是,VBA-JSON库需要正确导入,并且可能需要额外的引用,如Microsoft Scripting Runtime,否则可能在运行时出错。此外,使用HTMLFile对象时,某些安全设置或环境可能导致CreateObject失败,需要提醒用户注意。手动解析方法的风险也需要强调,避免用户在不合适的场景下使用。 总结来说,我需要将用户提供的几种方法整理成三个部分,每个部分详细说明步骤和示例代码,正确添加引用,最后生成相关问题,确保用户能够按照步骤操作,并解决可能遇到的问题。</think>以下是Excel VBA解析JSON的三种常用方法及示例教程: ### 一、使用VBA-JSON解析器库(推荐) 1. **安装步骤** - 下载`JsonConverter.bas`文件[^2] - 在VBA编辑器中点击`文件 > 导入文件`添加模块 - 添加引用:`工具 > 引用 > Microsoft Scripting Runtime` 2. **基础解析代码** ```vba Sub ParseJSON() Dim jsonStr As String jsonStr = "{""name"":""Andy"", ""age"":30}" Set jsonObj = JsonConverter.ParseJson(jsonStr) MsgBox jsonObj("name") & " " & jsonObj("age") End Sub ``` 3. **解析JSON文件** ```vba Sub ReadJSONFile() Open "data.json" For Input As #1 jsonStr = Input$(LOF(1), 1) Close #1 Set jsonData = JsonConverter.ParseJson(jsonStr) End Sub ``` 特点:支持复杂结构解析,需依赖外部模块[^1] ### 二、利用HTMLFile对象(无依赖方案) ```vba Function JsonParse(jsonStr As String) As Object With CreateObject("HTMLFile") .parentWindow.execScript "var json = " & jsonStr, "JScript" Set JsonParse = .parentWindow.json End With End Function Sub Demo() Set data = JsonParse("{""items"":[{""id"":1}, {""id"":2}]}") MsgBox data.items(0).id End Sub ``` 原理:通过JScript引擎解析JSON,支持嵌套对象[^3][^4] ### 三、简单结构手动解析 ```vba Sub SimpleParse() jsonStr = "[{""id"":""2"", ""name"":""Andy""}]" jsonStr = Replace(Replace(jsonStr, "[{", ""), "}]", "") arr = Split(jsonStr, ",") For Each item In arr kv = Split(item, ":") Debug.Print Trim(kv(0)), Trim(Replace(kv(1), """", "")) Next End Sub ``` 适用场景:简单键值对提取,正则表达式更安全[^5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值