// Package filter ... // // Description : JSON 词法分析 // // Author : go_developer@163.com<白茶清欢> // // Date : 2022-07-04 17:52 package filter import ( "git.zhangdeman.cn/zhangdeman/util" "github.com/pkg/errors" ) // NewLexical 获取实例 // // Author : go_developer@163.com<白茶清欢> // // Date : 20:52 2022/7/4 func NewLexical(jsonData string) *lexical { return &lexical{ jsonData: jsonData, lexicalResult: make([]*lexicalNode, 0), } } // lexical 词法解析 // // Author : go_developer@163.com<白茶清欢> // // Date : 20:42 2022/7/4 type lexical struct { jsonData string keyLeftRightTokenCnt int lexicalResult []*lexicalNode } // Parse 解析词法 // // Author : go_developer@163.com<白茶清欢> // // Date : 18:11 2022/7/4 func (l *lexical) Parse(jsonData string) ([]*lexicalNode, error) { // mt.Println(jsonData) if len(jsonData) < 2 { return nil, errors.New("input data is not json") } tmpStr := "" for _, itemChar := range jsonData { currentChar := string(itemChar) tmpStrType := l.getTmpStrType(tmpStr) if l.inputCharIsToken(currentChar, tmpStr, tmpStrType) { if currentChar == keyLeftRightToken { // 双引号计数 l.keyLeftRightTokenCnt++ } // 是关键词 if len(tmpStr) > 0 { l.lexicalResult = append(l.lexicalResult, &lexicalNode{ Val: tmpStr, IsToken: false, }) } l.lexicalResult = append(l.lexicalResult, &lexicalNode{ Val: currentChar, IsToken: true, }) tmpStr = "" } else { // 不是关键词, 继续向后走 if currentChar == " " && l.keyLeftRightTokenCnt%2 == 0 { // 当前字符是空格, 只有在 "" 之间方才有效 , 关键字之间的空格, 忽略即可 continue } if (currentChar == "\n" || currentChar == "\t") && (l.keyLeftRightTokenCnt%2 == 0 || tmpStrType == "number" || tmpStrType == "bool") { // 数字或者 bool 之后的 \n \t 无意义 , 不在 "" 之间也无意义 continue } tmpStr = tmpStr + currentChar } } return l.lexicalResult, nil } // inputCharIsToken 输入字符是否为关键字 // // Author : go_developer@163.com<白茶清欢> // // Date : 18:15 2022/7/4 func (l *lexical) inputCharIsToken(inputChar string, tmpStr string, tmpStrType string) bool { tokenList := []string{ // list 类型起始 listLeftToken, // listRight list 类型结束 listRightToken, // 对象起始 objectLeftToken, // 对象结束 objectRightToken, // key 值的起始 keyLeftRightToken, // 转义符 // escapeCharacterToken, // 冒号 colonToken, // 逗号 commaToken, } preCheck := false for _, item := range tokenList { if item == inputChar { preCheck = true break } } if !preCheck { // 输入一定不是关键字 return false } if len(tmpStr) == 0 && inputChar == keyLeftRightToken { // 双引号关键字 return true } tmpStrByte := []byte(tmpStr) if len(tmpStrByte) > 0 { // 字符串拼接过程中, 只有 " 可能是关键字 // 判断转义符个数 escapeCharacterTokenCnt := 0 for i := len(tmpStrByte) - 1; i >= 0; i-- { if string(tmpStrByte[i]) == escapeCharacterToken { // 前一个是转义符 escapeCharacterTokenCnt++ continue } // 非连续的转义符不计数 break } // 0 个 或者 偶数个转义符, " 是关键字 if inputChar == keyLeftRightToken && escapeCharacterTokenCnt%2 == 0 { return true } } var preNode *lexicalNode if len(l.lexicalResult) > 0 { preNode = l.lexicalResult[len(l.lexicalResult)-1] } if inputChar == colonToken && nil != preNode && preNode.Val == keyLeftRightToken && l.keyLeftRightTokenCnt > 0 && l.keyLeftRightTokenCnt%2 == 0 { // : 必须出现在偶数 " 之后才是关键字 return true } // , 是关键字的场景 // {"name":"zhangsan", "age":"18"} // [{"name":"zhangsan", "age":"18"}, {"name":"zhangsan", "age":"18"}] // [[],[]] // [1,2,3] // [true,false,true] if inputChar == commaToken && len(tmpStr) == 0 && ( // 对应 {"name":"zhangsan", "age":"18"} (nil != preNode && preNode.Val == keyLeftRightToken) || // 对应[{"name":"zhangsan", "age":"18"}, {"name":"zhangsan", "age":"18"}] (nil != preNode && preNode.Val == objectRightToken) || // 对应[[],[]] (nil != preNode && preNode.Val == listRightToken) || // 对应 [true,false,true] / [1,2,3] / [1,true,2,false] (nil != preNode && (preNode.Val == listLeftToken || preNode.Val == commaToken) && (tmpStrType == "number" || tmpStr == "bool"))) { // 对应 return true } // { 可能出现的情况 // {} // [{}] [{}, {}] // {"person": {}} if inputChar == objectLeftToken && len(tmpStr) == 0 && (nil == preNode || // 对应 {} (nil != preNode && preNode.Val == listLeftToken) || // 对应 [{}] (nil != preNode && preNode.Val == colonToken) || // 对应 {"person": {}} (nil != preNode && preNode.Val == commaToken)) { // 对应 [{}, {}] // { 是关键字 return true } // } 可能出出现的情况 // {} // [{}] // {"name":"zhangsan"} // {"person": {"name":"zhangsan"}} // {"person": {"name":"zhangsan", "age": 18}} // {"person": {"work":true}} // {"person": {"like":1}} // {"person_list": [{"like":1}]} if inputChar == objectRightToken && len(tmpStr) == 0 && ( // 对应 {}, [{}] (nil != preNode && preNode.Val == objectLeftToken) || // 对应 {"name":"zhangsan"} (nil != preNode && preNode.Val == keyLeftRightToken) || // 对应 {"person": {"name":"zhangsan"}} (nil != preNode && preNode.Val == objectRightToken)) || // 对应 {"person": {"work":true}} / {"person": {"like":1}} (nil != preNode && preNode.Val == colonToken && (tmpStr == "number" || tmpStr == "bool")) || // 对应 {"person_list": [{"like":1}]} (nil != preNode && preNode.Val == listRightToken) { // } 是关键字 return true } // [ 可能出现的场景 // [] // [[],[]] // "a": [] if inputChar == listLeftToken && len(tmpStr) == 0 && (nil == preNode || // 对应 [] (nil != preNode && preNode.Val == listLeftToken) || // 对应 [[],[]] (nil != preNode && preNode.Val == colonToken)) { // 对应 "a": [] // [ 是关键字 return true } // ] 可能出现的场景 // [] // [[],[]] // [{}, {}] // [1,2,3] // [true, false] // ["", "" ] if inputChar == listRightToken && len(tmpStr) == 0 && ( //对应 [] (nil != preNode && preNode.Val == listLeftToken) || // 对应 [[],[]] (nil != preNode && preNode.Val == listRightToken) || // [true, false] / (nil != preNode && preNode.Val == objectRightToken) || // 对应 [{}, {}] / [1,2,3] (nil != preNode && (tmpStrType == "number" || tmpStrType == "bool")) || // 对应 ["", "" ] (nil != preNode && preNode.Val == keyLeftRightToken)) { return true } return false } func (l *lexical) getTmpStrType(tmpStr string) string { var preNode *lexicalNode if len(l.lexicalResult) > 0 { preNode = l.lexicalResult[len(l.lexicalResult)-1] } // 数字只有一个场景 {"age": 18},多以若是数字, 则 前一次解析必为关键字 : if nil != preNode && preNode.Val == colonToken { // 判断是否可转数字 var floatVal float64 if err := util.ConvertAssign(&floatVal, tmpStr); nil == err { return "number" } } // 判断是否为 bool // bool 只有一个场景 {"work": true/false},多以若是数字, 则 前一次解析必为关键字 : if nil != preNode && preNode.Val == colonToken { // 判断是否可转数字 var boolVal bool if err := util.ConvertAssign(&boolVal, tmpStr); nil == err { return "bool" } } return "string" }