不器小窝 不器小窝
首页
随笔
  • GoLang
  • 前端

    • Vue
    • VuePress
  • 开发工具
  • 系统工具
读吧
  • ToDb (opens new window)
  • goKit (opens new window)
  • 友情链接
  • 免费信息
  • 时间线
  • 关于
GitHub (opens new window)

不器

知行合一
首页
随笔
  • GoLang
  • 前端

    • Vue
    • VuePress
  • 开发工具
  • 系统工具
读吧
  • ToDb (opens new window)
  • goKit (opens new window)
  • 友情链接
  • 免费信息
  • 时间线
  • 关于
GitHub (opens new window)
  • GoLang

    • GoLang 安装
    • Go经验
    • 错误集合
    • 基础库

    • 第三方库

      • colly
      • gjson
      • cron
      • sjson
        • gjson库简介
        • 开源库地址
        • 快速使用
          • 安装
          • 使用
          • 支持的类型
          • 修改数组
          • 删除
          • 错误处理
          • 非法JSON串
          • 非法键路径
        • 实战
          • 将多层json转换成单层json
        • 注意事项
    • Fyne

    • Wails

    • GoFrame

  • Dart

  • Markdown

  • 语言
  • GoLang
  • 第三方库
不器
2023-02-04
目录

sjson

# gjson库简介

SJSON是一个Go包,它提供了一种在json文档中设置值的非常快速和简单的方法。要快速检索json值,请查看GJSON。

# 开源库地址

tidwall/sjson: Set JSON values very quickly in Go (opens new window)

# 快速使用

# 安装

go get github.com/tidwall/sjson
1

# 使用

package main

import (
  "fmt"

  "github.com/tidwall/sjson"
)

const json = `{"name":{"first":"li","last":"dj"},"age":18}`

func main() {
  value, _ := sjson.Set(json, "name.last", "dajun")
  fmt.Println(value)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

上面代码通过sjson.Set()将JSON串中name.last对应的值设置为dajun。与gjson一样,sjson也通过键路径指定具体的位置,键路径即为一系列以.分隔的键。sjson支持的键路径语法是gjson的一个子集,具体键路径的语法可以参见上一篇文章。sjson.Set()返回设置之后的JSON串。最终程序输出:

{
    "name": {
        "first": "li",
        "last": "dajun"
    },
    "age": 18
}
1
2
3
4
5
6
7

# 支持的类型

sjson支持的类型包括nil/bool/int/float/string等。如果传入sjson不支持的类型,sjson会调用json.Marshal,然后将生成的字符串设置到对应的键路径上:

type User struct {
  Name string `json:"name"`
  Age  int    `json:"age"`
}

func main() {
  nilJSON, _ := sjson.Set("", "key", nil)
  fmt.Println(nilJSON)

  boolJSON, _ := sjson.Set("", "key", false)
  fmt.Println(boolJSON)

  intJSON, _ := sjson.Set("", "key", 1)
  fmt.Println(intJSON)

  floatJSON, _ := sjson.Set("", "key", 10.5)
  fmt.Println(floatJSON)

  strJSON, _ := sjson.Set("", "key", "hello")
  fmt.Println(strJSON)

  mapJSON, _ := sjson.Set("", "key", map[string]interface{}{"hello": "world"})
  fmt.Println(mapJSON)

  u := User{Name: "dj", Age: 18}
  structJSON, _ := sjson.Set("", "key", u)
  fmt.Println(structJSON)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

注意,我们传入一个空字符串,sjson.Set()会生成一个空对象,然后按照键路径依次设置值。下面分析上述程序输出:

  • nil:在JSON中用null表示,输出{"key":null};

  • false:在JSON中布尔值用true/false表示,输出{"key":false};

  • 1和10.5:整数和浮点数在JSON中都用number表示,分别输出{"key":1}和{"key":10.5};

  • hello:输出{"key":"hello"};

  • map[string]interface{}:sjson并不原生支持map类型,故通过json.Marshal将其序列化为{"hello":"world"}再设置到键key上,输出{"key":{"hello":"world"}};

  • User对象:先通过json.Marshal序列化为{"name":"dj","age":18}再设置;

# 修改数组

修改数组可以通过在键路径后添加索引,有两种特殊情况:

  • 使用-1或数组长度为索引表示在数组后添加一个新元素;

  • 使用的索引超出数组的长度,会在数组中添加很多null值。

看下面示例:

func main() {
  fruits := `{"fruits":["apple", "orange", "banana"]}`

  var newValue string
  newValue, _ = sjson.Set(fruits, "fruits.1", "grape")
  fmt.Println(newValue)

  newValue, _ = sjson.Set(fruits, "fruits.3", "pear")
  fmt.Println(newValue)

  newValue, _ = sjson.Set(fruits, "fruits.-1", "strawberry")
  fmt.Println(newValue)

  newValue, _ = sjson.Set(fruits, "fruits.5", "watermelon")
  fmt.Println(newValue)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • fruits.1:设置第二个水果为grape(索引从0开始),输出{"fruits":["apple", "grape", "banana"]};

  • fruits.3:由于数组长度为3,使用3表示在数组后添加一个元素,输出{"fruits":["apple","orange","banana","pear"]};

  • fruits.-1:使用-1同样表示在数组后添加一个元素,输出{"fruits":["apple", "orange", "banana","strawberry"]};

  • fruits.5:索引5已经大于数组长度3了,所以会多出两个null,输出{"fruits":["apple","orange","banana",null,null,"watermelon"]}。

# 删除

删除数组元素需要调用sjson.Delete()方法,键路径语法相同。如果键路径对应的值不存在,则Delete()无效果:

func main() {
  var newValue string
  user := `{"name":{"first":"li","last":"dj"},"age":18}`

  newValue, _ = sjson.Delete(user, "name.first")
  fmt.Println(newValue)

  newValue, _ = sjson.Delete(user, "name.full")
  fmt.Println(newValue)

  fruits := `{"fruits":["apple", "orange", "banana"]}`

  newValue, _ = sjson.Delete(fruits, "fruits.1")
  fmt.Println(newValue)

  newValue, _ = sjson.Delete(fruits, "fruits.-1")
  fmt.Println(newValue)

  newValue, _ = sjson.Delete(fruits, "fruits.5")
  fmt.Println(newValue)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  • name.first:删除字段name.first,输出{"name":{"last":"dj"},"age":18};

  • name.full:由于字段name.full不存在,无效果,输出{"name":{"first":"li","last":"dj"},"age":18};

  • fruits.1:删除数组fruits的第二个元素,输出{"fruits":["apple", "banana"]};

  • fruits.-1:删除数组最后一个元素,输出{"fruits":["apple", "orange"]};

  • fruits.5:索引5超出数组长度3,无效果,输出{"fruits":["apple", "orange", "banana"]}。

# 错误处理

使用sjson出现的错误分为两种,一种是传入的JSON串不是合法的串,另一种是键路径语法错误。Set()和Delete()方法返回的第二个参数为错误,只有非法的键路径会返回错误,非法JSON串不会。

# 非法JSON串

同gjson一样,sjson同样不会检查传入的JSON串的合法性,它假设传入的是合法的串。如果传入一个非法的JSON串,程序输出不确定的结果:

func main() {
  user := `{"name":dj,age:18}`
  newValue, err := sjson.Set(user, "name", "dajun")
  fmt.Println(err, newValue)
}
1
2
3
4
5

上面程序中,我故意传入一个非法的 JSON 串(dj和age漏掉了双引号)。最终程序输出:

<nil> {"name":dj,age:"dajun"}
1

将age变为了dajun,显然不正确。然而此时返回的err = nil。

# 非法键路径

与gjson相比,sjson能使用的键路径语法比较有限,不能使用通配符和一些条件语法。如果传入的键路径非法,将返回非空的错误值:

func main() {
  user := `{"name":"dj","age":18}`
  newValue, err := sjson.Set(user, "na?e", "dajun")
  fmt.Println(err, newValue)
}
1
2
3
4
5

上次使用通配符?,输出:

wildcard characters not allowed in path 
1

# 实战

# 将多层json转换成单层json

jsonStr := `
{
    "重庆": {
        "重庆": "1"
    },
    "陕西": {
        "宝鸡": "2",
        "榆林": "3",
        "铜川": "4"
    },
    "内蒙古": {
        "呼和浩特": "5",
        "巴彦淖尔": "6"
    },
    "安徽": {
        "宿州": "7",
        "芜湖": "8"
    }
}
`
// 用于装载json的map
jsonMap := make(map[string]interface{})
// 将json数据转换为json对象
json.Unmarshal([]byte(jsonStr), &jsonMap)

newJsonStr := ""
for _, provinceCity := range jsonMap {
	for cityName, cityCode := range provinceCity.(map[string]interface{}) {
		newJsonStr, _ = sjson.Set(newJsonStr, cityName, cityCode)
	}
}
fmt.Println(newJsonStr)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

输出

{
    "重庆": "1",
    "宝鸡": "2",
    "榆林": "3",
    "铜川": "4",
    "呼和浩特": "5",
    "巴彦淖尔": "6",
    "宿州": "7",
    "芜湖": "8"
}
1
2
3
4
5
6
7
8
9
10

# 注意事项

  1. 如果想把json字符串加入到某个节点下,json字符串的"会被转译变成\",此问题暂时无解
更新时间: 2023/2/10 09:50:10
cron
Hello Fyne

← cron Hello Fyne→

最近更新
01
Vue-pure-Admin基础
03-16
02
WebStorm工具使用手册
03-15
03
Windows
03-12
更多文章>
Theme by Vdoing | Copyright © 2022-2023 不器 | 小窝
sitemap icon by Icons8
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式