Golang学习篇——提取Json数据中指定字段数据

1. json-iterator介绍

jsoniter ( json-iterator )是一款快且灵活的 JSON 解析器,同时提供 Java 和 Go 两个版本Jsoniter 是最快的 JSON 解析.

器。它最多能比普通的解析器快 10 倍之多,即使在数据绑定的用法下也有同样的性能优势。

Golang中的跑分图(来自网上):

2. 提取Json指定字段数据

有时候,例如一个JSON对象很大,字段很多,完全没有必要为了访问几个字段而创建一个很大的实体类对象,我们只想解析json数据中的某个字段做业务处理,代码如下:

package main

import (
	"fmt"
	"github.com/json-iterator/go"
	"strconv"
	"strings"
)

//方法1:获取指定字段的值(参数1:字段路径,参数2:解析后的数据)
func GetJsonFieldValue(path []string, data map[string]interface{}) (interface{}, bool) {
	if v, ok := data[path[0]]; ok == true {
		if len(path) == 1 {
			return v, true
		} else {
			value, ok := GetJsonFieldValue(path[1:], v.(map[string]interface{}))
			return value, ok
		}
	} else {
		return nil, false
	}
}

//方法2:获取指定字段的值(参数1:字段路径,参数2:原始json数据)
func GetFieldFromJson(path []string, value []byte) string {
	var temp jsoniter.Any
	for i, v := range path {
		if i == 0 {
			temp = jsoniter.Get(value, v)
			if temp == nil {
				return ""
			}
		} else {
			temp = temp.Get(v)
			if temp == nil {
				return ""
			}
		}
	}

	switch temp.ValueType() {
	case jsoniter.InvalidValue, jsoniter.NilValue, jsoniter.BoolValue, jsoniter.ArrayValue, jsoniter.ObjectValue:
		return ""
	case jsoniter.StringValue:
		return temp.ToString()
	case jsoniter.NumberValue:
		return strconv.Itoa(temp.ToInt())
	}
	return ""
}

func main() {
	//通过原始数据获取指定字段值
	res := GetFieldFromJson(strings.Split("commonInfo/Sent", "/"), []byte(Data))
	fmt.Println("res:", res)

	dataBuf := make(map[string]interface{})
	err := jsoniter.Unmarshal([]byte(Data), &dataBuf)
	if err != nil {
		fmt.Printf("Json解析失败!error info:%v\n", err, Data)
		return
	}
	fmt.Println("Json解析成功")

	//1层
	value, ok := dataBuf["SentType"].(string)
	if ok {
		fmt.Println("value:", value)
	} else {
		fmt.Println("not found")
	}
	filed1 := "SentType"
	value1, ok := GetJsonFieldValue(strings.Split(filed1, "/"), dataBuf)
	if ok {
		fmt.Println("value1:", value1.(string))
	} else {
		fmt.Println("value1 not found")
	}

	//多层
	filed2 := "commonInfo/Sent"
	value2, ok := GetJsonFieldValue(strings.Split(filed2, "/"), dataBuf)
	if ok {
		fmt.Println("value2:", value2.(string))
	} else {
		fmt.Println("value2 not found")
	}
}

var Data = `{
	"SentType": "11",
	"commonInfo": {
		"operationId": "10012019111317041700000001",
		"operationType": 1,
		"operationTime": "2019-11-13 17:04:17",
		"dataSource": null,
		"Sent": "10"
	},
	"metaData": {
		"type": 1,
		"name": "常口",
		"libraryId": null,
		"libraryMark": "1",
		"data": [
			{
				"id": "218906b5-f483-4beb-b8e7-b5b589cf4a40",
				"credentialNumber": "7787ce86c379a6d0f604569b7e3ea1ea",
				"cityCode": null,
				"presentPlace": "650103",
				"communityCode": null,
				"createDate": "2019-11-13 17:04:17",
				"name": "房",
				"featureValue": "",
				"algorithmCode": "0204000100",
				"picType": 1,
				"picBase64": "",
				"picUrl": "11",
				"CRUD": 1
			}
		]
	},
	"timeLists": [
		{
			"serverID": "1001",
			"serverName": "黑名单",
			"startTime": "2019-11-13 17:04:17 278",
			"sendTime": "2019-11-13 17:04:17 278"
		}
	]
}`

运行结果:

res: 10
Json解析成功
value: 11
value1: 11
value2: 10

总结:建议使用方法2,无需判断原始字段类型,最终返回string。

### 后端同时处理普通数据JSON数据的方法 为了使后端能够同时处理普通数据JSON数据,可以采用不同的策略来区分这两种类型的输入。一种常见的方式是在HTTP请求的不同部分放置不同类型的数据。 #### 使用URL参数传递普通数据 对于简单的键对形式的普通数据,可以通过GET方法中的查询字符串或POST/PUT/PATCH方法中的表单编码(`application/x-www-form-urlencoded` 或 `multipart/form-data`)来进行传输[^4]。这种方式适用于少量且结构简单的小型数据集。 例如,在发送一个带有额外整数ID字段以及嵌套对象作为主体内容的请求时: ```http POST /api/resource?userId=123 HTTP/1.1 Content-Type: application/json;charset=UTF-8 { "roles": { "id": 4, "name": "admin" } } ``` 这里`userId`被附加到了URL路径后面作为一个查询参数,而复杂的JSON对象则放在了消息体里。 #### 结合使用@PathVariable, @RequestParam与@RequestBody注解 在Spring Boot应用程序中,如果要在一个控制器方法内同时获取来自不同位置的数据,则可以根据其来源分别应用相应的注解[@PathVariable](用于路径变量),[@RequestParam](针对查询参数) 和 [@RequestBody](解析实体正文)[^2]。 考虑这样一个场景:有一个API允许更新用户的个人信息,并接受用户ID作为URI的一部分,其他属性通过JSON格式提交至服务器;此时可以在Java代码中这样定义路由处理器函数: ```java @PostMapping("/users/{userId}") public ResponseEntity<String> updateUser( @PathVariable Long userId, @RequestParam(required=false) Boolean activeStatus, @RequestBody UserDetail detail){ // 更新逻辑... return new ResponseEntity<>("User updated successfully.", HttpStatus.OK); } ``` 上述示例展示了如何组合这些注解以满足需求——其中`{userId}`是从URL提取出来的路径片段,`activeStatus`可能是一个可选的布尔标志位,最后整个`detail`对象则是由客户端发来的JSON序列化而来的内容。 #### Gin框架下的实现方案 对于Golang开发者来说,当使用像[Gin](https://github.com/gin-gonic/gin)这样的Web开发库构建RESTful API服务的时候,也可以轻松地做到这一点。只需要确保HTML模板正确设置了表单项的名字以便于识别它们是属于哪一部分的数据流即可[^3]。 假设我们正在创建一个支持上传文件的同时还能携带一些元信息(比如用户名、邮箱地址等)的功能模块,那么对应的Go语言代码可能会看起来像是这样子: ```go func handleForm(c *gin.Context) { var formData struct { Name string `form:"name"` Email string `form:"email"` Metadata map[string]interface{} `json:"metadata"` } if err := c.ShouldBind(&formData); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error":err.Error()}) return } file, _ := c.FormFile("file") // ... 文件保存操作 ... c.String(http.StatusOK, fmt.Sprintf("Received %s's data and file", formData.Name)) } ``` 在这个例子里面,`ShouldBind()`会自动尝试从多个地方读取所需的信息并将之绑定到指定的目标结构体内。这使得即使混合着多种形式的数据也能得到妥善管理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值