• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

davidhoo / jsonpath / 17030864931

18 Aug 2025 04:19AM UTC coverage: 82.264% (+0.3%) from 81.95%
17030864931

Pull #4

github

web-flow
Merge cdfd8f658 into 5afbb86b5
Pull Request #4: Fix issue #3: 解决 JSONPath 多字段子节点查询问题

97 of 112 new or added lines in 2 files covered. (86.61%)

1 existing line in 1 file now uncovered.

1962 of 2385 relevant lines covered (82.26%)

50.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.37
/parser.go
1
package jsonpath
2

3
import (
4
        "fmt"
5
        "regexp"
6
        "strconv"
7
        "strings"
8
)
9

10
// 解析 JSONPath 表达式
11
func parse(path string) ([]segment, error) {
180✔
12
        // 处理空路径
180✔
13
        if path == "" {
180✔
14
                return nil, nil
×
15
        }
×
16

17
        // 检查并移除 $ 前缀
18
        if !strings.HasPrefix(path, "$") {
180✔
19
                return nil, NewError(ErrSyntax, "path must start with $", path)
×
20
        }
×
21
        path = strings.TrimPrefix(path, "$")
180✔
22

180✔
23
        // 如果路径只有 $,返回空段列表
180✔
24
        if path == "" {
180✔
25
                return nil, nil
×
26
        }
×
27

28
        // 移除前导点
29
        path = strings.TrimPrefix(path, ".")
180✔
30

180✔
31
        // 处理递归下降
180✔
32
        if strings.HasPrefix(path, ".") {
180✔
33
                return parseRecursive(path[1:])
×
34
        }
×
35

36
        // 处理常规路径
37
        return parseRegular(path)
180✔
38
}
39

40
// 解析递归下降路径
41
func parseRecursive(path string) ([]segment, error) {
14✔
42
        var segments []segment
14✔
43
        segments = append(segments, &recursiveSegment{})
14✔
44

14✔
45
        // 如果路径为空,直接返回
14✔
46
        if path == "" {
16✔
47
                return segments, nil
2✔
48
        }
2✔
49

50
        // 移除前导点
51
        path = strings.TrimPrefix(path, ".")
12✔
52

12✔
53
        // 如果还有路径,继续解析
12✔
54
        if path != "" {
24✔
55
                remainingSegments, err := parseRegular(path)
12✔
56
                if err != nil {
14✔
57
                        return nil, err
2✔
58
                }
2✔
59
                segments = append(segments, remainingSegments...)
10✔
60
        }
61

62
        return segments, nil
10✔
63
}
64

65
// 解析常规路径
66
func parseRegular(path string) ([]segment, error) {
192✔
67
        var segments []segment
192✔
68
        var current string
192✔
69
        var inBracket bool
192✔
70
        var bracketContent string
192✔
71

192✔
72
        for i := 0; i < len(path); i++ {
3,570✔
73
                char := path[i]
3,378✔
74

3,378✔
75
                switch {
3,378✔
76
                case char == '[':
68✔
77
                        if inBracket {
68✔
78
                                return nil, NewError(ErrSyntax, "nested brackets not allowed", path)
×
79
                        }
×
80
                        if current != "" {
126✔
81
                                seg, err := createDotSegment(current)
58✔
82
                                if err != nil {
58✔
83
                                        return nil, err
×
84
                                }
×
85
                                segments = append(segments, seg)
58✔
86
                                current = ""
58✔
87
                        }
88
                        inBracket = true
68✔
89

90
                case char == ']':
68✔
91
                        if !inBracket {
68✔
92
                                return nil, NewError(ErrSyntax, "unexpected closing bracket", path)
×
93
                        }
×
94
                        seg, err := parseBracketSegment(bracketContent)
68✔
95
                        if err != nil {
76✔
96
                                return nil, err
8✔
97
                        }
8✔
98
                        segments = append(segments, seg)
60✔
99
                        bracketContent = ""
60✔
100
                        inBracket = false
60✔
101

102
                case char == '.' && !inBracket:
128✔
103
                        if current != "" {
252✔
104
                                seg, err := createDotSegment(current)
124✔
105
                                if err != nil {
124✔
106
                                        return nil, err
×
107
                                }
×
108
                                segments = append(segments, seg)
124✔
109
                                current = ""
124✔
110
                        }
111

112
                default:
3,114✔
113
                        if inBracket {
4,336✔
114
                                bracketContent += string(char)
1,222✔
115
                        } else {
3,114✔
116
                                current += string(char)
1,892✔
117
                        }
1,892✔
118
                }
119
        }
120

121
        // 处理最后一个段
122
        if inBracket {
184✔
123
                return nil, NewError(ErrSyntax, "unclosed bracket", path)
×
124
        }
×
125
        if current != "" {
316✔
126
                seg, err := createDotSegment(current)
132✔
127
                if err != nil {
132✔
128
                        return nil, err
×
129
                }
×
130
                segments = append(segments, seg)
132✔
131
        }
132

133
        return segments, nil
184✔
134
}
135

136
// 创建点表示法段
137
func createDotSegment(name string) (segment, error) {
314✔
138
        if name == "*" {
316✔
139
                return &wildcardSegment{}, nil
2✔
140
        }
2✔
141
        return &nameSegment{name: name}, nil
312✔
142
}
143

144
// 解析方括号段
145
func parseBracketSegment(content string) (segment, error) {
68✔
146
        // 处理通配符
68✔
147
        if content == "*" {
74✔
148
                return &wildcardSegment{}, nil
6✔
149
        }
6✔
150

151
        // 处理过滤器表达式
152
        if strings.HasPrefix(content, "?") {
114✔
153
                return parseFilterSegment(content[1:])
52✔
154
        }
52✔
155

156
        // 处理多索引选择或多字段选择
157
        if strings.Contains(content, ",") ||
10✔
158
                ((strings.HasPrefix(content, "'") && strings.HasSuffix(content, "'")) && strings.Contains(content[1:len(content)-1], "','")) ||
10✔
159
                ((strings.HasPrefix(content, "\"") && strings.HasSuffix(content, "\"")) && strings.Contains(content[1:len(content)-1], "\",\"")) {
14✔
160
                return parseMultiIndexSegment(content)
4✔
161
        }
4✔
162

163
        // 处理切片表达式
164
        if strings.Contains(content, ":") {
6✔
165
                return parseSliceSegment(content)
×
166
        }
×
167

168
        // 处理索引或名称
169
        return parseIndexOrName(content)
6✔
170
}
171

172
// 标准化过滤器表达式
173
func normalizeFilterExpression(expr string) string {
50✔
174
        expr = strings.TrimSpace(expr)
50✔
175
        // 移除括号
50✔
176
        if strings.HasPrefix(expr, "(") && strings.HasSuffix(expr, ")") {
50✔
177
                expr = strings.TrimSpace(expr[1 : len(expr)-1])
×
178
        }
×
179
        return expr
50✔
180
}
181

182
// 应用 De Morgan 定律转换表达式
183
func applyDeMorgan(expr string) (string, error) {
2✔
184
        // 处理空表达式
2✔
185
        if expr == "" {
2✔
186
                return "", NewError(ErrInvalidFilter, "empty expression", expr)
×
187
        }
×
188

189
        // 如果表达式被括号包围,先移除括号
190
        if strings.HasPrefix(expr, "(") && strings.HasSuffix(expr, ")") {
2✔
191
                expr = strings.TrimSpace(expr[1 : len(expr)-1])
×
192
        }
×
193

194
        // 解析表达式
195
        conditions, operators, err := splitLogicalOperators(expr)
2✔
196
        if err != nil {
2✔
197
                return "", err
×
198
        }
×
199

200
        // 转换每个条件
201
        for i := range conditions {
6✔
202
                cond := strings.TrimSpace(conditions[i])
4✔
203
                // 如果条件本身是一个复合表达式(带括号),递归处理
4✔
204
                if strings.HasPrefix(cond, "(") && strings.HasSuffix(cond, ")") {
4✔
205
                        inner, err := applyDeMorgan(cond)
×
206
                        if err != nil {
×
207
                                return "", err
×
208
                        }
×
209
                        conditions[i] = inner
×
210
                        continue
×
211
                }
212

213
                // 确保条件以 @ 开头
214
                if !strings.HasPrefix(cond, "@") {
4✔
215
                        if strings.HasPrefix(cond, ".") {
×
216
                                cond = "@" + cond
×
217
                        } else {
×
218
                                return "", NewError(ErrInvalidFilter, fmt.Sprintf("invalid condition: %s", cond), expr)
×
219
                        }
×
220
                }
221

222
                // 反转比较操作符
223
                for _, op := range []string{"<=", ">=", "==", "!=", "<", ">"} {
22✔
224
                        if idx := strings.Index(cond, op); idx != -1 {
22✔
225
                                prefix := cond[:idx]
4✔
226
                                suffix := cond[idx+len(op):]
4✔
227
                                var newOp string
4✔
228
                                switch op {
4✔
229
                                case "==":
2✔
230
                                        newOp = "!="
2✔
231
                                case "!=":
×
232
                                        newOp = "=="
×
233
                                case "<":
×
234
                                        newOp = ">="
×
235
                                case "<=":
×
236
                                        newOp = ">"
×
237
                                case ">":
2✔
238
                                        newOp = "<="
2✔
239
                                case ">=":
×
240
                                        newOp = "<"
×
241
                                }
242
                                conditions[i] = prefix + newOp + suffix
4✔
243
                                break
4✔
244
                        }
245
                }
246
        }
247

248
        // 转换逻辑运算符
249
        for i := range operators {
4✔
250
                switch operators[i] {
2✔
251
                case "&&":
×
252
                        operators[i] = "||"
×
253
                case "||":
2✔
254
                        operators[i] = "&&"
2✔
255
                }
256
        }
257

258
        // 重建表达式
259
        var result strings.Builder
2✔
260
        for i, cond := range conditions {
6✔
261
                if i > 0 {
6✔
262
                        result.WriteString(" " + operators[i-1] + " ")
2✔
263
                }
2✔
264
                // 如果条件包含空格或逻辑运算符,需要加括号
265
                if strings.Contains(cond, " ") || strings.Contains(cond, "&&") || strings.Contains(cond, "||") {
4✔
266
                        result.WriteString("(" + cond + ")")
×
267
                } else {
4✔
268
                        result.WriteString(cond)
4✔
269
                }
4✔
270
        }
271

272
        return result.String(), nil
2✔
273
}
274

275
// 解析过滤器表达式
276
func parseFilterSegment(content string) (segment, error) {
52✔
277
        // 检查语法
52✔
278
        if !strings.HasPrefix(content, "@") && !strings.HasPrefix(content, "(@") && !strings.HasPrefix(content, "!") && !strings.HasPrefix(content, "(!") {
54✔
279
                return nil, NewError(ErrInvalidFilter, fmt.Sprintf("invalid filter syntax: %s", content), content)
2✔
280
        }
2✔
281

282
        // 取过滤器内容
283
        var isNegated bool
50✔
284
        var filterContent string
50✔
285
        var isCompoundNegation bool
50✔
286

50✔
287
        switch {
50✔
288
        case strings.HasPrefix(content, "(!"):
×
289
                if !strings.HasSuffix(content, ")") {
×
290
                        return nil, NewError(ErrInvalidFilter, "invalid filter syntax: missing closing parenthesis", content)
×
291
                }
×
292
                isNegated = true
×
293
                isCompoundNegation = true
×
294
                filterContent = content[2 : len(content)-1]
×
295
        case strings.HasPrefix(content, "!@"):
4✔
296
                isNegated = true
4✔
297
                filterContent = content[1:]
4✔
298
        case strings.HasPrefix(content, "(@"):
10✔
299
                if !strings.HasSuffix(content, ")") {
10✔
300
                        return nil, NewError(ErrInvalidFilter, "invalid filter syntax: missing closing parenthesis", content)
×
301
                }
×
302
                filterContent = content[2 : len(content)-1]
10✔
303
        case strings.HasPrefix(content, "@"):
34✔
304
                filterContent = content
34✔
305
        case strings.HasPrefix(content, "!"):
2✔
306
                isNegated = true
2✔
307
                filterContent = content[1:]
2✔
308
                if strings.HasPrefix(filterContent, "(") {
4✔
309
                        if !strings.HasSuffix(filterContent, ")") {
2✔
310
                                return nil, NewError(ErrInvalidFilter, "invalid filter syntax: missing closing parenthesis", content)
×
311
                        }
×
312
                        isCompoundNegation = true
2✔
313
                        filterContent = filterContent[1 : len(filterContent)-1]
2✔
314
                }
315
        default:
×
316
                filterContent = content
×
317
        }
318

319
        // 如果是复合否定表达式,应用 De Morgan 定律
320
        if isNegated && isCompoundNegation {
52✔
321
                var err error
2✔
322
                filterContent, err = applyDeMorgan(filterContent)
2✔
323
                if err != nil {
2✔
324
                        return nil, NewError(ErrInvalidFilter, fmt.Sprintf("error applying De Morgan's laws: %v", err), content)
×
325
                }
×
326
                isNegated = false // 已经处理过否定
2✔
327
        }
328

329
        // 标准化表达式
330
        filterContent = normalizeFilterExpression(filterContent)
50✔
331

50✔
332
        // 分割逻辑运算符
50✔
333
        conditions, operators, err := splitLogicalOperators(filterContent)
50✔
334
        if err != nil {
50✔
335
                return nil, NewError(ErrInvalidFilter, fmt.Sprintf("error splitting logical operators: %v", err), content)
×
336
        }
×
337

338
        // 解析每个条件
339
        filterConditions := make([]filterCondition, 0, len(conditions))
50✔
340
        for i, condStr := range conditions {
118✔
341
                // 处理括号内的条件
68✔
342
                if strings.HasPrefix(condStr, "(") && strings.HasSuffix(condStr, ")") {
70✔
343
                        // 递归处理括号内的条件
2✔
344
                        innerContent := strings.TrimSpace(condStr[1 : len(condStr)-1])
2✔
345
                        innerConditions, innerOperators, err := splitLogicalOperators(innerContent)
2✔
346
                        if err != nil {
2✔
347
                                return nil, NewError(ErrInvalidFilter, fmt.Sprintf("error parsing inner conditions: %v", err), content)
×
348
                        }
×
349

350
                        // 解析内部条件
351
                        for j, innerCondStr := range innerConditions {
6✔
352
                                cond, err := parseFilterCondition(strings.TrimSpace(innerCondStr))
4✔
353
                                if err != nil {
4✔
354
                                        return nil, err
×
355
                                }
×
356
                                filterConditions = append(filterConditions, cond)
4✔
357
                                if j < len(innerOperators) {
6✔
358
                                        operators = append(operators, innerOperators[j])
2✔
359
                                }
2✔
360
                        }
361
                } else {
66✔
362
                        // 解析普通条件
66✔
363
                        cond, err := parseFilterCondition(strings.TrimSpace(condStr))
66✔
364
                        if err != nil {
72✔
365
                                return nil, err
6✔
366
                        }
6✔
367
                        // 如果是简单否定表达式,应用否定到第一个条件
368
                        if isNegated && i == 0 {
64✔
369
                                switch cond.operator {
4✔
370
                                case "==":
4✔
371
                                        cond.operator = "!="
4✔
372
                                case "!=":
×
373
                                        cond.operator = "=="
×
374
                                case "<":
×
375
                                        cond.operator = ">="
×
376
                                case "<=":
×
377
                                        cond.operator = ">"
×
378
                                case ">":
×
379
                                        cond.operator = "<="
×
380
                                case ">=":
×
381
                                        cond.operator = "<"
×
382
                                }
383
                                isNegated = false // 已经处理过否定
4✔
384
                        }
385
                        filterConditions = append(filterConditions, cond)
60✔
386
                }
387
        }
388

389
        return &filterSegment{
44✔
390
                conditions: filterConditions,
44✔
391
                operators:  operators,
44✔
392
        }, nil
44✔
393
}
394

395
// 解析过滤器条件
396
func parseFilterCondition(content string) (filterCondition, error) {
70✔
397
        // 确保条件以 @ 开头
70✔
398
        if !strings.HasPrefix(content, "@") {
80✔
399
                if strings.HasPrefix(content, ".") {
20✔
400
                        content = "@" + content
10✔
401
                } else {
10✔
402
                        return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("invalid condition: %s", content), content)
×
403
                }
×
404
        }
405

406
        // 检查是否是函数调用
407
        if strings.Contains(content, ".match(") {
74✔
408
                parts := strings.Split(content, ".match(")
4✔
409
                if len(parts) != 2 || !strings.HasSuffix(parts[1], ")") {
4✔
410
                        return filterCondition{}, NewError(ErrInvalidFilter, "invalid match function syntax", content)
×
411
                }
×
412

413
                field := strings.TrimSpace(parts[0])
4✔
414
                pattern := strings.TrimSpace(parts[1][:len(parts[1])-1])
4✔
415

4✔
416
                // 验证字段格式
4✔
417
                if !strings.HasPrefix(field, "@") {
4✔
418
                        return filterCondition{}, NewError(ErrInvalidFilter, "filter condition must start with @", content)
×
419
                }
×
420

421
                // 移除引号
422
                pattern = strings.Trim(pattern, "\"'")
4✔
423

4✔
424
                return filterCondition{
4✔
425
                        field:    strings.TrimPrefix(field, "@."),
4✔
426
                        operator: "match",
4✔
427
                        value:    pattern,
4✔
428
                }, nil
4✔
429
        }
430

431
        // 查找比较操作符
432
        var operator string
66✔
433
        var operatorIndex int
66✔
434
        var operatorFound bool
66✔
435

66✔
436
        // 按长度排序的操作符列表,确保先匹配较长的操作符
66✔
437
        operators := []string{"<=", ">=", "==", "!=", "<", ">"}
66✔
438
        for _, op := range operators {
340✔
439
                idx := strings.Index(content, op)
274✔
440
                if idx != -1 {
338✔
441
                        // 确保这是一个独立的操作符,不是符串值的一部分
64✔
442
                        inQuotes := false
64✔
443
                        inParens := 0
64✔
444
                        isValid := true
64✔
445
                        for i := 0; i < idx; i++ {
550✔
446
                                switch content[i] {
486✔
447
                                case '"':
×
448
                                        inQuotes = !inQuotes
×
449
                                case '(':
×
450
                                        inParens++
×
451
                                case ')':
×
452
                                        inParens--
×
453
                                }
454
                        }
455
                        if inQuotes || inParens > 0 {
64✔
456
                                isValid = false
×
457
                        }
×
458
                        if isValid {
128✔
459
                                operator = op
64✔
460
                                operatorIndex = idx
64✔
461
                                operatorFound = true
64✔
462
                                break
64✔
463
                        }
464
                }
465
        }
466

467
        if !operatorFound {
68✔
468
                return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("no valid operator found in condition: %s", content), content)
2✔
469
        }
2✔
470

471
        // 分割路径和值
472
        field := strings.TrimSpace(content[:operatorIndex])
64✔
473
        value := strings.TrimSpace(content[operatorIndex+len(operator):])
64✔
474

64✔
475
        // 解析值
64✔
476
        parsedValue, err := parseFilterValue(value)
64✔
477
        if err != nil {
68✔
478
                return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("invalid value: %s", value), content)
4✔
479
        }
4✔
480

481
        return filterCondition{
60✔
482
                field:    strings.TrimPrefix(field, "@."),
60✔
483
                operator: operator,
60✔
484
                value:    parsedValue,
60✔
485
        }, nil
60✔
486
}
487

488
// compareValues compares two values based on the operator
489
func compareValues(value1 interface{}, operator string, value2 interface{}) (bool, error) {
236✔
490
        // 检查操作符是否有效
236✔
491
        validOperators := map[string]bool{
236✔
492
                "==":    true,
236✔
493
                "!=":    true,
236✔
494
                ">":     true,
236✔
495
                "<":     true,
236✔
496
                ">=":    true,
236✔
497
                "<=":    true,
236✔
498
                "match": true,
236✔
499
        }
236✔
500
        if !validOperators[operator] {
238✔
501
                return false, fmt.Errorf("invalid operator: %s", operator)
2✔
502
        }
2✔
503

504
        // 处理 nil 值
505
        if value1 == nil || value2 == nil {
240✔
506
                switch operator {
6✔
507
                case "==":
6✔
508
                        return value1 == value2, nil
6✔
509
                case "!=":
×
510
                        return value1 != value2, nil
×
511
                default:
×
512
                        return false, fmt.Errorf("invalid operator for nil values: %s", operator)
×
513
                }
514
        }
515

516
        // 处理数字类型
517
        num1, num2, isNum := normalizeNumbers(value1, value2)
228✔
518
        if isNum {
350✔
519
                switch operator {
122✔
520
                case "==":
12✔
521
                        return num1 == num2, nil
12✔
522
                case "!=":
×
523
                        return num1 != num2, nil
×
524
                case ">":
58✔
525
                        return num1 > num2, nil
58✔
526
                case "<":
44✔
527
                        return num1 < num2, nil
44✔
528
                case ">=":
×
529
                        return num1 >= num2, nil
×
530
                case "<=":
8✔
531
                        return num1 <= num2, nil
8✔
532
                default:
×
533
                        return false, fmt.Errorf("invalid operator for numbers: %s", operator)
×
534
                }
535
        }
536

537
        // 处理字符串类型
538
        if str1, ok := value1.(string); ok {
190✔
539
                if str2, ok := value2.(string); ok {
168✔
540
                        switch operator {
84✔
541
                        case "==":
56✔
542
                                return str1 == str2, nil
56✔
543
                        case "!=":
22✔
544
                                return str1 != str2, nil
22✔
545
                        case ">":
6✔
546
                                return str1 > str2, nil
6✔
547
                        case "<":
×
548
                                return str1 < str2, nil
×
549
                        case ">=":
×
550
                                return str1 >= str2, nil
×
551
                        case "<=":
×
552
                                return str1 <= str2, nil
×
553
                        case "match":
×
554
                                re, err := regexp.Compile(str2)
×
555
                                if err != nil {
×
556
                                        return false, fmt.Errorf("invalid regex pattern: %s", str2)
×
557
                                }
×
558
                                return re.MatchString(str1), nil
×
559
                        default:
×
560
                                return false, fmt.Errorf("invalid operator for strings: %s", operator)
×
561
                        }
562
                }
563
                if operator == "match" {
×
564
                        return false, fmt.Errorf("pattern must be a string")
×
565
                }
×
566
        }
567
        if operator == "match" {
22✔
568
                return false, fmt.Errorf("value must be a string")
×
569
        }
×
570

571
        // 处理布尔类型
572
        if bool1, ok := value1.(bool); ok {
44✔
573
                if bool2, ok := value2.(bool); ok {
44✔
574
                        switch operator {
22✔
575
                        case "==":
22✔
576
                                return bool1 == bool2, nil
22✔
577
                        case "!=":
×
578
                                return bool1 != bool2, nil
×
579
                        default:
×
580
                                return false, fmt.Errorf("invalid operator for booleans: %s", operator)
×
581
                        }
582
                }
583
        }
584

585
        return false, fmt.Errorf("incompatible types for comparison")
×
586
}
587

588
// normalizeNumbers 将两个值转换为 float64 类型
589
func normalizeNumbers(value1, value2 interface{}) (float64, float64, bool) {
228✔
590
        var num1, num2 float64
228✔
591
        var ok1, ok2 bool
228✔
592

228✔
593
        // 尝试将 value1 转换为 float64
228✔
594
        switch v := value1.(type) {
228✔
595
        case float64:
122✔
596
                num1, ok1 = v, true
122✔
597
        case int64:
×
598
                num1, ok1 = float64(v), true
×
599
        case int:
×
600
                num1, ok1 = float64(v), true
×
601
        }
602

603
        // 尝试将 value2 转换为 float64
604
        switch v := value2.(type) {
228✔
605
        case float64:
122✔
606
                num2, ok2 = v, true
122✔
607
        case int64:
×
608
                num2, ok2 = float64(v), true
×
609
        case int:
×
610
                num2, ok2 = float64(v), true
×
611
        }
612

613
        return num1, num2, ok1 && ok2
228✔
614
}
615

616
// compareStrings compares two strings using the specified operator
617
func compareStrings(a string, operator string, b string) bool {
40✔
618
        return standardCompareStrings(a, operator, b)
40✔
619
}
40✔
620

621
// getFieldValue 获取对象中指定字段的值
622
func getFieldValue(obj interface{}, field string) (interface{}, error) {
256✔
623
        // 移除 @ 和前导点
256✔
624
        field = strings.TrimPrefix(field, "@")
256✔
625
        field = strings.TrimPrefix(field, ".")
256✔
626

256✔
627
        // 如果字段为空,返回对象本身
256✔
628
        if field == "" {
256✔
629
                return obj, nil
×
630
        }
×
631

632
        // 分割字段路径
633
        parts := strings.Split(field, ".")
256✔
634
        current := obj
256✔
635

256✔
636
        for _, part := range parts {
520✔
637
                // 确保当前是对象
264✔
638
                m, ok := current.(map[string]interface{})
264✔
639
                if !ok {
264✔
640
                        return nil, fmt.Errorf("value is not an object")
×
641
                }
×
642

643
                // 获取下一级字段值
644
                var exists bool
264✔
645
                current, exists = m[part]
264✔
646
                if !exists {
266✔
647
                        return nil, fmt.Errorf("field %s not found", part)
2✔
648
                }
2✔
649
        }
650

651
        return current, nil
254✔
652
}
653

654
// isValidIdentifier 检查字符串是否是有效的标识符
655
func isValidIdentifier(s string) bool {
6✔
656
        if s == "" {
6✔
NEW
657
                return false
×
NEW
658
        }
×
659

660
        // 检查第一个字符是否是字母或下划线
661
        first := s[0]
6✔
662
        if !((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z') || first == '_') {
6✔
NEW
663
                return false
×
NEW
664
        }
×
665

666
        // 检查其余字符是否是字母、数字或下划线
667
        for i := 1; i < len(s); i++ {
16✔
668
                c := s[i]
10✔
669
                if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') {
10✔
NEW
670
                        return false
×
NEW
671
                }
×
672
        }
673

674
        return true
6✔
675
}
676

677
// 解析多索引选择
678
func parseMultiIndexSegment(content string) (segment, error) {
32✔
679
        // 检查前导和尾随逗号
32✔
680
        if strings.HasPrefix(content, ",") {
34✔
681
                return nil, NewError(ErrInvalidPath, "leading comma in multi-index segment", content)
2✔
682
        }
2✔
683
        if strings.HasSuffix(content, ",") {
32✔
684
                return nil, NewError(ErrInvalidPath, "trailing comma in multi-index segment", content)
2✔
685
        }
2✔
686

687
        parts := strings.Split(content, ",")
28✔
688

28✔
689
        // 检查空索引
28✔
690
        for _, part := range parts {
96✔
691
                if strings.TrimSpace(part) == "" {
70✔
692
                        return nil, NewError(ErrInvalidPath, "empty index in multi-index segment", content)
2✔
693
                }
2✔
694
        }
695

696
        // 检查是否包含字符串字段名
697
        // 检查是否包含字符串字段名
698
        hasString := false
26✔
699
        hasQuotedString := false
26✔
700
        for _, part := range parts {
74✔
701
                trimmed := strings.TrimSpace(part)
48✔
702
                // 检查是否是字符串字段名(带引号)
48✔
703
                if (strings.HasPrefix(trimmed, "'") && strings.HasSuffix(trimmed, "'")) ||
48✔
704
                        (strings.HasPrefix(trimmed, "\"") && strings.HasSuffix(trimmed, "\"")) {
60✔
705
                        hasString = true
12✔
706
                        hasQuotedString = true
12✔
707
                        break
12✔
708
                }
709
                // 检查是否是非数字的字段名
710
                if _, err := strconv.Atoi(trimmed); err != nil {
40✔
711
                        // 如果不是带引号的字符串,但也不是数字,可能是无效索引或不带引号的字段名
4✔
712
                        // 我们需要进一步判断
4✔
713
                        hasString = true
4✔
714
                        break
4✔
715
                }
716
        }
717

718
        // 如果有引号字符串,或者所有非数字部分都是有效的字段名,则作为多字段段处理
719
        if hasString && hasQuotedString {
38✔
720
                // 有引号字符串,按多字段段处理
12✔
721
                names := make([]string, 0, len(parts))
12✔
722
                for _, part := range parts {
36✔
723
                        trimmed := strings.TrimSpace(part)
24✔
724
                        // 处理带引号的字符串
24✔
725
                        if (strings.HasPrefix(trimmed, "'") && strings.HasSuffix(trimmed, "'")) ||
24✔
726
                                (strings.HasPrefix(trimmed, "\"") && strings.HasSuffix(trimmed, "\"")) {
46✔
727
                                names = append(names, trimmed[1:len(trimmed)-1])
22✔
728
                        } else {
24✔
729
                                // 处理不带引号的字段名
2✔
730
                                names = append(names, trimmed)
2✔
731
                        }
2✔
732
                }
733
                return &multiNameSegment{names: names}, nil
12✔
734
        }
735

736
        // 检查是否所有部分都是有效的字段名(不带引号但也不是纯数字)
737
        if hasString && !hasQuotedString {
18✔
738
                // 检查是否所有部分都是数字或者所有部分都是有效的标识符
4✔
739
                allNumbers := true
4✔
740
                allValidNames := true
4✔
741

4✔
742
                for _, part := range parts {
14✔
743
                        trimmed := strings.TrimSpace(part)
10✔
744
                        if _, err := strconv.Atoi(trimmed); err != nil {
16✔
745
                                // 不是数字
6✔
746
                                allNumbers = false
6✔
747
                                if !isValidIdentifier(trimmed) {
6✔
NEW
748
                                        allValidNames = false
×
NEW
749
                                }
×
750
                        } else {
4✔
751
                                // 是数字,但在混合情况下不应该作为字段名
4✔
752
                                allValidNames = false
4✔
753
                        }
4✔
754
                }
755

756
                // 如果混合了数字和非数字,这是无效的
757
                if !allNumbers && !allValidNames {
6✔
758
                        return nil, NewError(ErrInvalidPath, "cannot mix numeric indices and field names", content)
2✔
759
                }
2✔
760

761
                if allValidNames {
4✔
762
                        // 所有部分都是有效标识符,按多字段段处理
2✔
763
                        names := make([]string, 0, len(parts))
2✔
764
                        for _, part := range parts {
6✔
765
                                trimmed := strings.TrimSpace(part)
4✔
766
                                names = append(names, trimmed)
4✔
767
                        }
4✔
768
                        return &multiNameSegment{names: names}, nil
2✔
769
                }
770
        }
771

772
        // 否则解析为多索引段
773
        indices := make([]int, 0, len(parts))
10✔
774
        for _, part := range parts {
40✔
775
                trimmed := strings.TrimSpace(part)
30✔
776
                idx, err := strconv.Atoi(trimmed)
30✔
777
                if err != nil {
30✔
NEW
778
                        return nil, NewError(ErrInvalidPath, fmt.Sprintf("invalid index: %s", trimmed), content)
×
UNCOV
779
                }
×
780
                indices = append(indices, idx)
30✔
781
        }
782

783
        return &multiIndexSegment{indices: indices}, nil
10✔
784
}
785

786
// 解析切片表达式
787
func parseSliceSegment(content string) (segment, error) {
28✔
788
        parts := strings.Split(content, ":")
28✔
789
        if len(parts) > 3 {
30✔
790
                return nil, fmt.Errorf("invalid slice syntax")
2✔
791
        }
2✔
792

793
        slice := &sliceSegment{start: 0, end: 0, step: 1}
26✔
794

26✔
795
        // 解析起始索引
26✔
796
        if parts[0] != "" {
46✔
797
                start, err := strconv.Atoi(parts[0])
20✔
798
                if err != nil {
22✔
799
                        return nil, fmt.Errorf("invalid start index: %s", parts[0])
2✔
800
                }
2✔
801
                slice.start = start
18✔
802
        }
803

804
        // 解析结束索引
805
        if len(parts) > 1 && parts[1] != "" {
42✔
806
                end, err := strconv.Atoi(parts[1])
18✔
807
                if err != nil {
20✔
808
                        return nil, fmt.Errorf("invalid end index: %s", parts[1])
2✔
809
                }
2✔
810
                slice.end = end
16✔
811
        }
812

813
        // 解析步长
814
        if len(parts) > 2 && parts[2] != "" {
34✔
815
                step, err := strconv.Atoi(parts[2])
12✔
816
                if err != nil {
14✔
817
                        return nil, fmt.Errorf("invalid step: %s", parts[2])
2✔
818
                }
2✔
819
                if step == 0 {
12✔
820
                        return nil, fmt.Errorf("step cannot be zero")
2✔
821
                }
2✔
822
                slice.step = step
8✔
823
        }
824

825
        return slice, nil
18✔
826
}
827

828
// 解析索引或名称
829
func parseIndexOrName(content string) (segment, error) {
66✔
830
        // 处理函数调用
66✔
831
        if strings.HasSuffix(content, ")") {
74✔
832
                return parseFunctionCall(content)
8✔
833
        }
8✔
834

835
        // 尝试解析为数字索引
836
        if idx, err := strconv.Atoi(content); err == nil {
72✔
837
                return &indexSegment{index: idx}, nil
14✔
838
        }
14✔
839

840
        // 处理字符串字面量
841
        if strings.HasPrefix(content, "'") && strings.HasSuffix(content, "'") && len(content) > 1 {
58✔
842
                return &nameSegment{name: content[1 : len(content)-1]}, nil
14✔
843
        }
14✔
844

845
        // 处理双引号字符串字面量
846
        if strings.HasPrefix(content, "\"") && strings.HasSuffix(content, "\"") && len(content) > 1 {
42✔
847
                return &nameSegment{name: content[1 : len(content)-1]}, nil
12✔
848
        }
12✔
849

850
        return &nameSegment{name: content}, nil
18✔
851
}
852

853
// 解析函数调用
854
func parseFunctionCall(content string) (segment, error) {
52✔
855
        // 找到函数名和参数列表
52✔
856
        openParen := strings.Index(content, "(")
52✔
857
        if openParen == -1 {
54✔
858
                return nil, fmt.Errorf("invalid function call syntax: missing opening parenthesis")
2✔
859
        }
2✔
860

861
        // 检查闭合括号
862
        if !strings.HasSuffix(content, ")") {
52✔
863
                return nil, fmt.Errorf("invalid function call syntax: missing closing parenthesis")
2✔
864
        }
2✔
865

866
        name := content[:openParen]
48✔
867
        argsStr := content[openParen+1 : len(content)-1]
48✔
868

48✔
869
        // 解析参数
48✔
870
        args := make([]interface{}, 0)
48✔
871
        if argsStr != "" {
88✔
872
                // 简单参数解析,暂时只支持数字和字符串
40✔
873
                argParts := strings.Split(argsStr, ",")
40✔
874
                for _, arg := range argParts {
108✔
875
                        arg = strings.TrimSpace(arg)
68✔
876
                        if arg == "" {
70✔
877
                                continue // 跳过空参数
2✔
878
                        }
879
                        // 尝试解析为数字
880
                        if num, err := strconv.ParseFloat(arg, 64); err == nil {
100✔
881
                                args = append(args, num)
34✔
882
                                continue
34✔
883
                        }
884
                        // 处理字符串参数
885
                        if strings.HasPrefix(arg, "'") && strings.HasSuffix(arg, "'") {
58✔
886
                                // 处理转义引号
26✔
887
                                str := arg[1 : len(arg)-1]
26✔
888
                                str = strings.ReplaceAll(str, "''", "'")
26✔
889
                                args = append(args, str)
26✔
890
                                continue
26✔
891
                        }
892
                        return nil, fmt.Errorf("unsupported argument type: %s", arg)
6✔
893
                }
894
        }
895

896
        return &functionSegment{name: name, args: args}, nil
42✔
897
}
898

899
// 分割逻辑运算符
900
func splitLogicalOperators(expr string) ([]string, []string, error) {
54✔
901
        var conditions []string
54✔
902
        var operators []string
54✔
903
        var currentCondition strings.Builder
54✔
904
        inQuotes := false
54✔
905
        inParens := 0
54✔
906
        i := 0
54✔
907

54✔
908
        for i < len(expr) {
1,194✔
909
                // 处理引号内的内容
1,140✔
910
                if expr[i] == '"' || expr[i] == '\'' {
1,196✔
911
                        inQuotes = !inQuotes
56✔
912
                        currentCondition.WriteByte(expr[i])
56✔
913
                        i++
56✔
914
                        continue
56✔
915
                }
916

917
                // 处理括号
918
                if expr[i] == '(' {
1,090✔
919
                        inParens++
6✔
920
                        currentCondition.WriteByte(expr[i])
6✔
921
                        i++
6✔
922
                        continue
6✔
923
                }
924
                if expr[i] == ')' {
1,084✔
925
                        inParens--
6✔
926
                        if inParens < 0 {
6✔
927
                                return nil, nil, NewError(ErrInvalidFilter, "unmatched closing parenthesis", expr)
×
928
                        }
×
929
                        currentCondition.WriteByte(expr[i])
6✔
930
                        i++
6✔
931
                        continue
6✔
932
                }
933

934
                // 检查逻辑运算符
935
                if !inQuotes && inParens == 0 {
1,906✔
936
                        if i+1 < len(expr) {
1,630✔
937
                                op := expr[i : i+2]
796✔
938
                                if op == "&&" || op == "||" {
818✔
939
                                        // 加当前条件
22✔
940
                                        cond := strings.TrimSpace(currentCondition.String())
22✔
941
                                        if cond == "" {
22✔
942
                                                return nil, nil, NewError(ErrInvalidFilter, "empty condition before operator", expr)
×
943
                                        }
×
944
                                        conditions = append(conditions, cond)
22✔
945
                                        operators = append(operators, op)
22✔
946
                                        currentCondition.Reset()
22✔
947
                                        i += 2
22✔
948
                                        continue
22✔
949
                                }
950
                        }
951
                }
952

953
                currentCondition.WriteByte(expr[i])
1,050✔
954
                i++
1,050✔
955
        }
956

957
        // 检查最终状态
958
        if inQuotes {
54✔
959
                return nil, nil, NewError(ErrInvalidFilter, "unclosed quotes", expr)
×
960
        }
×
961
        if inParens != 0 {
54✔
962
                return nil, nil, NewError(ErrInvalidFilter, "unmatched parentheses", expr)
×
963
        }
×
964

965
        // 添加最后一个条件
966
        lastCond := strings.TrimSpace(currentCondition.String())
54✔
967
        if lastCond == "" {
54✔
968
                return nil, nil, NewError(ErrInvalidFilter, "empty condition at end", expr)
×
969
        }
×
970
        conditions = append(conditions, lastCond)
54✔
971

54✔
972
        // 验证条件和运算符的数量关系
54✔
973
        if len(conditions) != len(operators)+1 {
54✔
974
                return nil, nil, NewError(ErrInvalidFilter, "invalid number of conditions and operators", expr)
×
975
        }
×
976

977
        return conditions, operators, nil
54✔
978
}
979

980
// parseFilterValue parses a filter value string into an appropriate type
981
func parseFilterValue(valueStr string) (interface{}, error) {
64✔
982
        valueStr = strings.TrimSpace(valueStr)
64✔
983

64✔
984
        // 处理 null
64✔
985
        if valueStr == "null" {
66✔
986
                return nil, nil
2✔
987
        }
2✔
988

989
        // 处理布尔值
990
        if valueStr == "true" {
68✔
991
                return true, nil
6✔
992
        }
6✔
993
        if valueStr == "false" {
56✔
994
                return false, nil
×
995
        }
×
996

997
        // 处理字符串(带引号)
998
        if (strings.HasPrefix(valueStr, "'") && strings.HasSuffix(valueStr, "'")) ||
56✔
999
                (strings.HasPrefix(valueStr, "\"") && strings.HasSuffix(valueStr, "\"")) {
78✔
1000
                return valueStr[1 : len(valueStr)-1], nil
22✔
1001
        }
22✔
1002

1003
        // 尝试解析为数字
1004
        if num, err := strconv.ParseFloat(valueStr, 64); err == nil {
64✔
1005
                return num, nil
30✔
1006
        }
30✔
1007

1008
        // 如果不是其他类型,返回错误
1009
        return nil, fmt.Errorf("invalid value: %s", valueStr)
4✔
1010
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc