go语言中反序列化的问题
与其说是反序列化的问题,倒不如说是一种解决反序列中导致的问题。
故事背景:php项目重构成go项目,其中一个接口中http调用了其他系统,返回了一个json串,其中一个字段结构如下:
"starring": {
"3041": "钟汉良",
"1043": "Angelababy",
"14182": "甘婷婷",
"3055004": "孙艺洲",
"1208": "于波",
"30145": "麦迪娜",
"3579404": "亓航",
"3062705": "邓莎",
"12358": "程皓枫",
"29028": "魏炳桦",
"3051663": "刘萌萌",
"3061404": "邹杨",
"3579641": "马程程"
},
经过一系列逻辑后要求返回的结构为:
starring:[钟汉良 Angelababy 甘婷婷 孙艺洲 于波 麦迪娜 亓航 邓莎 程皓枫 魏炳桦 刘萌萌 邹杨 马程程],
等于说把一个map结构变成一个数组结构,演员顺序不能变,按照主演的顺序。
起初重构的时候并没有考虑过这个问题,重构基本也是照葫芦画瓢,php中基本只有数组这一个结构,
和go对比下来用基本也都是用的map,
然而在测试进行测试的时候发现每次点进去发现演员顺序都会变,这就很尴尬了。
当然很容易联想到map随机的问题,那显然就是因为遍历的时候是无序的,
才会导致每次结果不一样。
按照原有php的逻辑就是,如果缓存没有就去请求其他系统,得到json串,然后存进redis,一般就会先反序列化,
这时候自然就变成map了,要再操作已经是无序了。
起初第一步想到的是,把结果拿下来反序列化的时候重写json方法,把这个json串的这个map结构替换成数组结构,
再存redis。这样就相当于改变了原有的redis里面的结构,这样是非常
不妥的,后面想到就是加入一个逻辑判断redis里面这个字段是map还是数组,相当于一个过渡方案,洗掉里面的数据,
因为redis里面数据时间只有14天。后面否定了这个选择,因为线上
的同时在运行的话或者其他系统有调用会产生冲突。
那么转换一下思路就是从三方系统拿到json串,用json.get的方法去取数据,而不是反序列化后再序列化,再存redis,
这样可以保留原有的结构,这样就是原汁原味的数据,然后同时做另外一个
map替换成数组的操作,用以后续的判断。
同理,当命中缓存时,拿到的数据也是有序的,也是做同样的处理,去返回数据,这样可以保证redis里面原有结构不会改变。
当然这其中也涉及到一个排序问题,就是如何从以上map的结构的json串变成下面这个有序数组的结构。
因为数据结构学的不是很好,查了一下网上的,也没有找到好的办法,用了比较笨的方法,strings包去截取字符串。
后面再向老大汇报这个重构问题时,提出了我的方案,并把源码给他看后,做了以下调整。
贴一下源码:
package main
import (
"encoding/json"
"fmt"
jsoniter "github.com/json-iterator/go"
"strings"
)
func main() {
//data:=jsoniter.Get(Body,"data").ToString()
//data:=string(Body)
// fmt.Println(data)
bodyMap := make(map[string]interface{}, 0)
// if err = jsoniter.Unmarshal(body, &albumInfo); err != nil {
_ = jsoniter.Unmarshal(Body, &bodyMap)
testData:=&Data{"data":bodyMap}
if err:= json.Unmarshal(Body,&testData);err!=nil{
fmt.Println(err)
}
//得到自定义序列化后的数据
fmt.Println(testData)
}
type Data map[string]interface{}
func (m *Data) MarshalJSON() ([]byte, error){
return nil ,nil
}
func(m *Data) UnmarshalJSON(body []byte) error{
bodyMap := make(map[string]interface{}, 0)
//解析到map
_= json.Unmarshal(body, &bodyMap)
//获取data
data := bodyMap["data"].([]interface{})
if len(data) != 0 {
for i, datum := range data {
starring:=jsoniter.Get(Body,"data",i,"starring").ToString()
//排序得到演员数组
starArr:=indexStar(starring)
datum.(map[string]interface{})["starring"]=starArr
//附加一个数组在原来的基础上
(*m)["data"].(map[string]interface{})["data"].([]interface{})[i]=datum
}}
return nil
}
func indexStar(star string) []string {
fmt.Println("star===",star)
strs := strings.Split(star,",")
fmt.Println(strs)
fmt.Println(len(strs))
starrings:=make([]string,0)
for i:= 0; i< len(strs) ; i++ {
strss := strings.Split(strs[i],`"`)
fmt.Println(strss[3])
starrings=append(starrings, strss[3])
}
fmt.Println(starrings)
return starrings
}
var Body =[]byte(`{
"callback": "",
"data": [
{
"albumPic": "http://i2.letvimg.com/lc05_isvrs/201603/23/16/34/1ed62530-1a8d-4495-9165-5a1f7125d476",
"albumType": {
"180001": "正片"
},
"category": {
"2": "电视剧"
},
"contentRating": {
"620005": "全年龄段"
},
"deleted": 0,
"directory": {
"3181": "鞠觉亮"
},
"downloadPlatform": {
"290005": "TV",
"290001": "Web",
"290002": "Pad",
"290003": "手机"
},
"episode": 62,
"gid": "126_10020129",
"id": 10020129,
"isEnd": 1,
"isHomemade": 0,
"nameCn": "孤芳不自赏",
"nowEpisodes": 62,
"nowIssue": 0,
"officialUrl": "http://tv.le.com/izt/gfbzs/index.html",
"payPlatform": {
"141010": "乐视电视(第三方)",
"141001": "Web",
"141011": "乐视电视盒子",
"141003": "安卓手机",
"141002": "pc客户端",
"141005": "Pad",
"141004": "超级手机",
"141007": "乐视电视",
"141006": "m站",
"141009": "apple iphone",
"141008": "apple ipad"
},
"picCollections": {
"90*120": "http://i3.letvimg.com/lc05_isvrs/201611/09/12/16/3d646a90-590e-48a2-a24c-5e52bdbd5b92.jpg",
"300*400": "http://i3.letvimg.com/lc07_isvrs/201611/09/12/16/7e457b77-7595-4fd5-96fa-fe3e625f9c79.jpg",
"120*90": "http://i1.letvimg.com/lc04_isvrs/201701/02/14/47/72aebcf4-65df-464b-b971-86f032c267ae.jpg",
"1080*608": "http://i0.letvimg.com/lc04_isvrs/201701/02/14/47/a4efbaf1-f848-4fc3-b912-7142ba6f69f1.jpg",
"128*96": "http://i2.letvimg.com/lc05_isvrs/201701/02/14/47/1edfe221-8287-48dd-8688-d829d10494e6.jpg",
"180*101": "http://i2.letvimg.com/lc07_isvrs/201701/02/14/47/91fbe360-75e7-4f50-afb9-69966250741c.jpg",
"180*135": "http://i3.letvimg.com/lc05_isvrs/201701/02/14/47/65ad6758-58cc-48e6-9070-b09c66e99aa4.jpg",
"120*160": "http://i1.letvimg.com/lc04_isvrs/201611/09/12/16/1ed5170e-5500-42b5-abfe-b6291284a70f.jpg",
"200*150": "http://i0.letvimg.com/lc06_isvrs/201701/02/14/47/24c2b8a2-da7b-42ac-8dcb-04b0d15b4db3.jpg",
"970*300": "http://i3.letvimg.com/lc07_isvrs/201702/01/11/26/6d2f5eaa-1d5a-4f98-9789-6a05e19de541.jpg",
"150*200": "http://i3.letvimg.com/lc04_isvrs/201611/09/12/16/b48bfffa-24c7-4ba3-8feb-493eecef3192.jpg",
"320*200": "http://i2.letvimg.com/lc05_isvrs/201701/02/14/47/d5ebd94e-2084-4ef7-ad01-ee00d82b0566.jpg",
"400*250": "http://i3.letvimg.com/lc02_isvrs/201701/02/14/47/8d2be7cf-0660-43fc-8da5-b9aed3da627d.jpg",
"600*800": "http://i1.letvimg.com/lc03_isvrs/202104/29/00/07/0cdcc7ec-4971-40aa-bf45-8df5414d02a5.jpg",
"132*99": "http://i2.letvimg.com/lc07_isvrs/201701/02/14/47/81b6e424-7535-4100-8953-3fa06032a5a7.jpg",
"400*300": "http://i3.letvimg.com/lc05_isvrs/201701/02/14/47/bca64b2d-9ebb-4497-b43f-425d628cfd04.jpg",
"400*225": "http://i1.letvimg.com/lc03_isvrs/201701/02/14/47/2b042e76-c2be-49bc-8942-f9bc9587fbd2.jpg",
"960*540": "http://i2.letvimg.com/lc06_isvrs/201701/02/14/47/03fd8e75-2246-44ed-8a7a-be07390b4911.jpg",
"96*128": "http://i2.letvimg.com/lc05_isvrs/201611/09/12/16/6f2d6ab9-ae98-47d4-97c0-f0b7a29c2a81.jpg",
"1440*810": "http://i0.letvimg.com/lc06_isvrs/201701/02/14/47/1855b8aa-ff1f-41cd-95b4-2c84943f10bc.jpg",
"160*120": "http://i3.letvimg.com/lc04_isvrs/201701/02/14/47/0c4324b4-3e79-47f1-8b39-c945e403c1c2.jpg"
},
"platformNowEpisodesNum": {
"420005": 62,
"all": 62,
"420003": 62,
"420007": 62,
"420001": 62
},
"platformVideoInfo": {
"420005": "62",
"all": "62",
"420003": "62",
"420007": "62",
"420001": "62"
},
"platformVideoNum": {
"420005": "627",
"420003": "627",
"420007": "609",
"420001": "627"
},
"playPlatform": {
"420005": "Pad",
"420003": "手机",
"420007": "TV",
"420001": "Web"
},
"relationId": "",
"releaseDate": "2017-01-02",
"score": 7.0,
"starring": {
"3041": "钟汉良",
"1043": "Angelababy",
"14182": "甘婷婷",
"3055004": "孙艺洲",
"1208": "于波",
"30145": "麦迪娜",
"3579404": "亓航",
"3062705": "邓莎",
"12358": "程皓枫",
"29028": "魏炳桦",
"3051663": "刘萌萌",
"3061404": "邹杨",
"3579641": "马程程"
},
"subCategory": {
"30042": "古装",
"30011": "爱情",
"30320": "偶像"
},
"subTitle": "钟汉良虐恋baby",
"thirdPartyId": ""
}
],
"msgs": [
"ok"
],
"result": 1,
"statusCode": ""
}`)
改进后源码:
package main
import (
"container/list"
"errors"
"fmt"
"github.com/json-iterator/go"
"log"
)
func main() {
//data:=jsoniter.Get(Body,"data").ToString()
//data:=string(Body)
// fmt.Println(data)
bodyMap := make(map[string]interface{}, 0)
// if err = jsoniter.Unmarshal(body, &albumInfo); err != nil {
if err := StarLinkedHashMap(Body, bodyMap); nil != err {
log.Fatal(err)
}
fmt.Println(bodyMap)
}
func StarLinkedHashMap(str []byte, v map[string]interface{}) error {
if err := jsoniter.Unmarshal(str, &v); nil != err {
return err
}
if _, ok := v["data"]; !ok {
return nil
}
_, ok := v["data"].([]interface{})
if !ok {
return nil
}
for idx, value := range v["data"].([]interface{}) {
starring := jsoniter.Get(str, "data", idx, "starring").ToString()
if len(starring) == 0 {
continue
}
keys, err := getKeys(starring)
if nil != err {
return err
}
stars := make([]interface{}, len(keys))
starMap := value.(map[string]interface{})["starring"].(map[string]interface{})
for i, key := range keys {
stars[i] = starMap[key]
}
v["data"].([]interface{})[idx].(map[string]interface{})["starring"] = stars
}
return nil
}
// getKeys 获取一级keys,未适配兼容性
func getKeys(str string) ([]string, error) {
l := list.New()
var keys []string
var cs []rune
isKey := true
for _, c := range str {
b := l.Back()
switch c {
case '"':
if nil != b && b.Value == '{' {
l.PushBack(c)
} else if b.Value == '"' {
l.Remove(b)
if len(cs) > 0 && isKey {
keys = append(keys, string(cs))
cs = []rune{}
}
} else {
return nil, errors.New("json format error")
}
case ':':
isKey = false
case ',':
isKey = true
case '[':
isKey = false
l.PushBack(c)
case '{':
isKey = true
l.PushBack(c)
case ']', '}':
if nil != b && b.Value != (c-2) {
return nil, errors.New("json format error")
}
l.Remove(b)
default:
// 控制只输出一层
if l.Len() == 2 && b.Value == '"' && isKey {
cs = append(cs, c)
}
}
}
return keys, nil
}
不难看出,我用的方法比较低级。改进过后用的是list的数据结构来实现的。
平时工作中几乎没有机会用到list这种结构,因此很有感触。。代码分析后续贴上。。