要求:需要根据用户传入的 jsonStr 中的name
和user_id
的顺序拼接其值。
代码:
package main
import (
"encoding/json"
"fmt"
"reflect"
)
func main() {
jsonStr := `{"name":"tom","user_id":"123"}` // 这是传入的参数,name 与 user_id 顺序不能确定先后
var str string
m := make(map[string]interface{})
_ = json.Unmarshal([]byte(jsonStr), &m)
v := reflect.ValueOf(m)
keys := v.MapKeys()
for _, key := range keys {
v1 := v.MapIndex(key).Interface().(string)
str += v1
}
fmt.Println(str)
// 由于 map 无序,不能固定输出:tom123
// 如何保持与 json 中键一致,固定输出?
// 比如若 json_str := `{"user_id":"123""name":"tom"}` 则输出 123tom
}
在线运行: https://play.golang.org/p/_ZMfsISpKWz
还请大佬们赐教,感激不尽。
1
Jason0803 2021-06-02 20:46:37 +08:00
为什么不用 struct
|
2
sunny352787 2021-06-02 20:48:07 +08:00
确定参数就用 struct,不确定就全取出来塞到 slice 里排序再用
|
3
hello2060 2021-06-02 20:51:19 +08:00
不懂 go, 但是你既然只能转到 map, map 里的 key 不是按照输入顺序排序, 那 map 是没法解决这问题的.
|
4
liyaojian OP @Jason0803 #1
@sunny352787 #2 struct 也不能确定用户传过来的 name 和 user_id 的顺序啊,我需要获取 name 和 user_id 对应顺序的值拼接字符串,不同的顺序拼接的字符串不一样 |
5
liyaojian OP @hello2060 #3 所以问下论坛里的大佬有没有其他方法解决这个问题,jsonStr 是用户的传参,这个不能变
|
6
virusdefender 2021-06-02 20:57:48 +08:00
如果只有这么两个字段的话,正则处理下就好了
|
7
lujjjh 2021-06-02 21:01:58 +08:00 1
很奇怪的需求,但也是有办法的: https://play.golang.org/p/PAYLlXZjhgF
|
8
Kisesy 2021-06-02 21:11:04 +08:00 1
|
9
sphawkcn 2021-06-02 21:19:41 +08:00
@lujjjh #7 在以前的 AAuto (现在叫 aardio )的圈子里,有个人跟你一样叫 lujjjh,请问是你吗?我的朋友
|
10
xiaoyiyu 2021-06-02 21:26:27 +08:00 1
struct + String() 不就可以了 https://play.golang.org/p/V6RXkf-sceA
|
11
hello2060 2021-06-02 21:30:35 +08:00
@liyaojian 如果只要能 work 就行, 你搜整个 string 看那个在前哪个在后就可以了, 搜"name":和"user_id":
|
12
hello2060 2021-06-02 21:35:07 +08:00 1
我不懂 go, 随便搜了下, 问题也没仔细看 https://play.golang.org/p/yZ5DxZLIMXC 这个是不是可以你看看
|
13
rekulas 2021-06-02 21:52:42 +08:00 1
很奇怪的需求 用第三方库可以容易的实现 采用类似其他语言的 loop 对象的 key 即可
``` package main import ( "github.com/tidwall/gjson" "log" ) type loop func(key gjson.Result,value gjson.Result) func main() { jsonStr := `{"name":"tom","user_id":"123"}` expectedResult := "" result := gjson.Parse(jsonStr) result.ForEach(func(key, value gjson.Result) bool { expectedResult += value.String() return true }) log.Println(expectedResult) } ``` 不过我敢肯定,这个功能的设计绝对是有问题的。。 |
14
rekulas 2021-06-02 21:56:18 +08:00
才发现跟#8 的重复了
|
15
SorcererXW 2021-06-02 21:57:51 +08:00 1
直接用正则表达式把所有 key 提取出来就能知道顺序了
https://stackoverflow.com/questions/24300112/regex-to-match-keys-in-json |
16
CEBBCAT 2021-06-02 22:02:01 +08:00 1
其实按照规范来说,JSON 的键值对是无序的[1],可以主张更换数据结构来规避这个“需要 JSON 的键按序解析”的问题。但假如这个用户比较顶……
再回到这个问题,map 的遍历在 Go 标准实现中也是无序的,所以楼主你这是在做无用功,或者说危险功。 我能想到的是定义一个 interface,实现一个 String()方法。然后在它的 UnmarshalJSON()中根据`"name"`和`"user_id"`的先后出现位置,来返回不同的 interface 。 1. https://stackoverflow.com/a/16870531 |
17
CEBBCAT 2021-06-02 22:03:27 +08:00
话说回来,我在这里祈祷楼主知道“XY 问题”这个名词,不要等到最后才说出真实的需求……
|
18
lujjjh 2021-06-02 22:19:21 +08:00
@sphawkcn 是我
@CEBBCAT 认同你的观点,既然规范里明确说了是无序的,就**不应该**依赖某种语言 /某种库下有序的特定实现。 Go 1 开始刻意把迭代 map 的顺序设计成随机,也是为了防止程序员依赖某个 Go 版本实现下的迭代顺序,而不同实现的迭代顺序是有可能不同的,就会造成可移植性的问题,索性设计成随机了。 顺便分享一个近期的故事: https://twitter.com/zty0826/status/1398477411000360960 |
19
hallDrawnel 2021-06-02 22:26:01 +08:00
把 key 复制出来放到 slice 排序后从 map 取值拼接。这是要做啥?某种奇怪的需要按照顺序的签名 or 校验操作?
|
21
wjfz 2021-06-02 22:56:55 +08:00
我这还有个骚操作,重新 marshal 一遍在 unMashal 出来就有序了。
``` package main import ( "encoding/json" "fmt" "reflect" ) func main() { jsonStr := `{"name":"tom","user_id":"123"}` // 这是传入的参数,name 与 user_id 顺序不能确定先后 var str string m := make(map[string]interface{}) _ = json.Unmarshal([]byte(jsonStr), &m) json1,_ := json.Marshal(m) _ = json.Unmarshal([]byte(json1), &m) v := reflect.ValueOf(m) keys := v.MapKeys() for _, key := range keys { v1 := v.MapIndex(key).Interface().(string) str += v1 } fmt.Println(str) // 由于 map 无序,不能固定输出:tom123 // 如何保持与 json 中键一致,固定输出? // 比如若 json_str := `{"user_id":"123""name":"tom"}` 则输出 123tom } ``` |
22
wjfz 2021-06-02 22:57:30 +08:00
如果是为了做签名,直接把 json 字符串拿去加密也是一种可选项。
|
23
labulaka521 2021-06-03 08:30:16 +08:00 via iPhone
像淘宝 pdd 的一些开放接口 key 的顺序都是字符序,
|
24
Muninn 2021-06-03 09:04:12 +08:00
The JSON Data Interchange Standard definition at json.org specifies that “An object is an unordered [emphasis mine] set of name/value pairs”, whereas an array is an “ordered collection of values”. In other words, by definition the order of the key/value pairs within JSON objects simply does not, and should not, matter.
这跟 map struct 根本没关系。因为 json 就是无序的,官方说你要有序你就用 array 。 楼主相当于要解析一个看起来像 json 但是不是 json 的东西,那只能自己解析了…… 只要是任何一个语言,用 json 库就是无序的。就算是有序的,也是实现的时候不小心有序了,将来随时可能无序。 |
25
bwangel 2021-06-03 14:00:33 +08:00
如果这是个语言问题,我比较赞同 8 楼的做法,gjson foreach 输的 json key,value 对是按照解析顺序输出的,比较满足你的需求。
如果这是个工程问题,我不建议使用 "github.com/tidwall/gjson",因为这样写了之后让代码更加晦涩难懂了,不利于维护。 在 json.encoder 一方看来,调整 json 中 map 的顺序完全不会有什么影响,因为这样做是符合 json 规范的,但是一调整就挂了。解决方案就是需要在代码中写个注释,说明顺序千万千万不能改,但是我们都知道,注释是及其不靠谱的,很多时候代码和注释完全对不上。 所以我的建议是 在 json 数据中加一个 order 字段,表明期望得到的顺序,这是一个示例 https://play.golang.org/p/c2DIY3q_vjR |
26
GTim 2021-06-03 19:27:38 +08:00
package main
import ( "encoding/json" "fmt" "reflect" "sort" ) func main() { jsonStr := `{"name":"tom","user_id":"123"}` var str string m := make(map[string]interface{}) _ = json.Unmarshal([]byte(jsonStr), &m) v := reflect.ValueOf(m) keys := make([]string, 0) keysMap := map[string]reflect.Value{} for _, key := range v.MapKeys() { keys = append(keys, key.String()) keysMap[key.String()] = key } sort.Strings(keys) for _, key := range keys { v1 := v.MapIndex(keysMap[key]).Interface().(string) str += v1 } fmt.Println(str) } |
28
AlexSLQ 2021-06-08 10:04:56 +08:00
为什么不用 struct.name+struct.userID 或者 map["name"]+map["userID"]这种,都确定就用这两种字段了就明着用呗
|
29
HUNYXV 2021-06-11 10:49:27 +08:00
https://play.golang.org/p/iyNvEWlS696
使用 struct 就好 然后实现 String() 接口 ```go type User struct { Name string `json:"name"` UserID string `json:"user_id"` } func (u *User) String() string { return fmt.Sprintf("%s%s", u.Name, u.UserID) } func main() { jsonStr := `{"name":"tom","user_id":"123"}` // 这是传入的参数,name 与 user_id 顺序不能确定先后 var user *User _ = json.Unmarshal([]byte(jsonStr), &user) fmt.Println(user) } ``` |
30
chenall 2021-07-10 08:51:16 +08:00 via Android
建行的聚合支付接口。
就是使用楼主这种逻辑进行签名。 我是直接提前把 key 的顺序做成一个有序列表。 然后再遍历。 只是这样子,后面有要扩展增加字段的时候,就要重新再修改这个列表。 |