JSON相关
基本用法
Escape开关
- 直接Json.Marshal: escapeHTML参数是true,即打开,会导致 &, <, 和 > (一般再url中) 变成 \u0026, \u003c, 和 \u003e
- 封装不进行escape的json marshal:
// JSONMarshal json序列化,escape设置为false func JSONMarshal(t interface{}) ([]byte, error) { buffer := &bytes.Buffer{} encoder := json.NewEncoder(buffer) encoder.SetEscapeHTML(false) err := encoder.Encode(t) return buffer.Bytes(), err }
uint64位精度缺失
// JSONUnMarshal 设置UseNumber,防止uint64位精度缺失
func JSONUnMarshal(jsonStream []byte, ret interface{}) error {
decoder := json.NewDecoder(strings.NewReader(string(jsonStream)))
decoder.UseNumber()
if err := decoder.Decode(&ret); err != nil {
fmt.Println("error:", err)
return err
}
return nil
}
- encoder还支持SetIndent(),设置是否缩进
案例记录
GoLang结构体转json,避免对[]byte进行base64
背景
golang中用protobuf 2处理,会生成如下格式结构体 :
type KbVideoInput struct {
KbArticleId []byte `protobuf:"bytes,1,opt,name=kb_article_id" json:"kb_article_id,omitempty"`
Title []byte `protobuf:"bytes,2,opt,name=title" json:"title,omitempty"`
Time *uint32 `protobuf:"varint,5,opt,name=time" json:"time,omitempty"`
NewIndexScore *float64 `protobuf:"fixed64,92,opt,name=new_index_score" json:"new_index_score,omitempty"`
}
- 对于[]byte类型的字段,如果直接通过json.marshal转换成json字符串(pb提供的jsonpb也是直接调用json.marshal),会对值进行base64处理。
- 往往不符合期望结果。
处理方案
为解决上述问题,通过两个函数:
- 遍历结构体,赋值结构体到map[string]interface{}中
- 将map中的[]byte转换为string,指针转换为基本数据类型
- 再进行json.marshal
代码记录:
- 根据json tag转换struct到map中
// 只解析struct第一层结构
func StuctToJsonMap(structData interface{}) *map[string]interface{} {
ret := make(map[string]interface{})
TypeData := reflect.TypeOf(structData).Elem()
ValueData := reflect.ValueOf(structData).Elem()
for i := 0; i < TypeData.NumField(); i++ {
fieldKey := TypeData.Field(i)
fieldVal := ValueData.Field(i)
tagKey, tagOp := parseTag(fieldKey.Tag.Get("json"))
if tagKey == "-" {
continue
}
//fmt.Printf("tagKey: %s, tag op: %s\n", tagKey, tagOp)
//fmt.Printf("fieldVal: %s, fieldVal.IsValid: %v\n", fieldVal, fieldVal.IsNil())
if !fieldVal.IsNil() {
ret[tagKey] = fieldVal.Interface()
} else {
if tagOp != "omitempty" {
ret[tagKey] = fieldVal.Interface()
}
}
}
return &ret
}
// tagOptions is the string following a comma in a struct field's "json"
// tag, or the empty string. It does not include the leading comma.
type tagOptions string
// parseTag splits a struct field's json tag into its name and
// comma-separated options.
func parseTag(tag string) (string, tagOptions) {
if idx := strings.Index(tag, ","); idx != -1 {
return tag[:idx], tagOptions(tag[idx+1:])
}
return tag, tagOptions("")
}
- map中的byte转string处理,防止json出现base64
// map中的byte转string处理,防止json出现base64
// map中的指针格式转成非指针
func FormatValue(dataMap *map[string]interface{}) {
for k, v := range *dataMap {
//fmt.Printf("key: %s, val: %+v, type: %s\n", k, v, reflect.TypeOf(v))
switch v.(type) {
case []byte:
(*dataMap)[k] = string(v.([]byte))
case json.RawMessage:
(*dataMap)[k] = string(v.(json.RawMessage))
case *uint32:
(*dataMap)[k] = *v.(*uint32)
case *int64:
(*dataMap)[k] = *v.(*int64)
case *int32:
(*dataMap)[k] = *v.(*int32)
case *string:
(*dataMap)[k] = *v.(*string)
default:
(*dataMap)[k] = v
}
}
}
- 使用
reqMap := es_helper.StuctToJsonMap(&reqStruct) // reqStruct为golang数据结构
FormatValue(reqMap)
nLog.Info("req data: %s", reqData)
reqJson, err := JsonMarshal(&reqMap)