Module: JSONP3

Defined in:
lib/json_p3.rb,
lib/json_p3/node.rb,
lib/json_p3/path.rb,
lib/json_p3/cache.rb,
lib/json_p3/lexer.rb,
lib/json_p3/patch.rb,
lib/json_p3/token.rb,
lib/json_p3/errors.rb,
lib/json_p3/filter.rb,
lib/json_p3/parser.rb,
lib/json_p3/pointer.rb,
lib/json_p3/segment.rb,
lib/json_p3/version.rb,
lib/json_p3/function.rb,
lib/json_p3/selector.rb,
lib/json_p3/unescape.rb,
lib/json_p3/serialize.rb,
lib/json_p3/environment.rb,
lib/json_p3/function_extensions/count.rb,
lib/json_p3/function_extensions/match.rb,
lib/json_p3/function_extensions/value.rb,
lib/json_p3/function_extensions/length.rb,
lib/json_p3/function_extensions/search.rb,
lib/json_p3/function_extensions/pattern.rb

Overview

rubocop:disable Style/Documentation

Defined Under Namespace

Classes: BooleanLiteral, ChildSegment, Count, EqExpression, Expression, FilterContext, FilterExpression, FilterExpressionLiteral, FilterSelector, FloatLiteral, FunctionExpression, FunctionExtension, GeExpression, GtExpression, IndexSelector, InfixExpression, IntegerLiteral, JSONPatch, JSONPatchError, JSONPatchTestFailure, JSONPath, JSONPathEnvironment, JSONPathEnvironmentError, JSONPathError, JSONPathNameError, JSONPathNode, JSONPathNodeList, JSONPathRecursionError, JSONPathSyntaxError, JSONPathTypeError, JSONPointer, JSONPointerError, JSONPointerIndexError, JSONPointerSyntaxError, JSONPointerTypeError, LRUCache, LeExpression, Length, Lexer, LogicalAndExpression, LogicalNotExpression, LogicalOrExpression, LtExpression, Match, NameSelector, NeExpression, NullLiteral, Op, OpAdd, OpCopy, OpMove, OpRemove, OpReplace, OpTest, Parser, Precedence, QueryExpression, RecursiveDescentSegment, RelativeJSONPointer, RelativeQueryExpression, RootQueryExpression, Search, Segment, Selector, SliceSelector, Stream, StringLiteral, SymbolNameSelector, Token, Value, WildcardSelector

Constant Summary collapse

DefaultEnvironment =
JSONPathEnvironment.new
VERSION =
"0.3.2"
TRANS =
{ "\\\"" => "\"", "'" => "\\'" }.freeze

Class Method Summary collapse

Class Method Details

.apply(ops, value) ⇒ Object



24
25
26
# File 'lib/json_p3.rb', line 24

def self.apply(ops, value)
  JSONPatch.new(ops).apply(value)
end

.canonical_string(value) ⇒ Object

Return value formatted as a canonical string literal.

Parameters:

  • value (String)


10
11
12
# File 'lib/json_p3/serialize.rb', line 10

def self.canonical_string(value)
  "'#{(JSON.dump(value)[1..-2] || raise).gsub(/('|\\")/, TRANS)}'"
end

.code_point_to_string(code_point, token) ⇒ Object



107
108
109
110
111
# File 'lib/json_p3/unescape.rb', line 107

def self.code_point_to_string(code_point, token)
  raise JSONPathSyntaxError.new("invalid character", token) if code_point <= 0x1F

  code_point.chr(Encoding::UTF_8)
end

.compile(path) ⇒ Object



16
17
18
# File 'lib/json_p3.rb', line 16

def self.compile(path)
  DefaultEnvironment.compile(path)
end

.decode_hex_char(value, index, token) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/json_p3/unescape.rb', line 54

def self.decode_hex_char(value, index, token)
  length = value.length

  raise JSONPathSyntaxError.new("incomplete escape sequence", token) if index + 4 >= length

  index += 1 # move past 'u'
  code_point = parse_hex_digits(value[index, 4], token)

  raise JSONPathSyntaxError.new("unexpected low surrogate", token) if low_surrogate?(code_point)

  return [code_point, index + 3] unless high_surrogate?(code_point)

  unless index + 9 < length && value[index + 4] == "\\" && value[index + 5] == "u"
    raise JSONPathSyntaxError.new("incomplete escape sequence", token)
  end

  low_surrogate = parse_hex_digits(value[index + 6, 10], token)

  raise JSONPathSyntaxError.new("unexpected low surrogate", token) unless low_surrogate?(low_surrogate)

  code_point = 0x10000 + (
    ((code_point & 0x03FF) << 10) | (low_surrogate & 0x03FF)
  )

  [code_point, index + 9]
end

.eq?(left, right) ⇒ Boolean

Returns:

  • (Boolean)


420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
# File 'lib/json_p3/filter.rb', line 420

def self.eq?(left, right)
  left = left.first.value if left.is_a?(JSONPathNodeList) && left.length == 1
  right = right.first.value if right.is_a?(JSONPathNodeList) && right.length == 1

  right, left = left, right if right.is_a?(JSONPathNodeList)

  if left.is_a? JSONPathNodeList
    return left == right if right.is_a? JSONPathNodeList
    return right == :nothing if left.empty?
    return left.first == right if left.length == 1

    return false
  end

  return true if left == :nothing && right == :nothing

  left == right
end

.find(path, data) ⇒ Object



12
13
14
# File 'lib/json_p3.rb', line 12

def self.find(path, data)
  DefaultEnvironment.find(path, data)
end

.high_surrogate?(code_point) ⇒ Boolean

Returns:

  • (Boolean)


99
100
101
# File 'lib/json_p3/unescape.rb', line 99

def self.high_surrogate?(code_point)
  code_point >= 0xD800 && code_point <= 0xDBFF
end

.low_surrogate?(code_point) ⇒ Boolean

Returns:

  • (Boolean)


103
104
105
# File 'lib/json_p3/unescape.rb', line 103

def self.low_surrogate?(code_point)
  code_point >= 0xDC00 && code_point <= 0xDFFF
end

.lt?(left, right) ⇒ Boolean

Returns:

  • (Boolean)


439
440
441
442
443
444
445
446
447
# File 'lib/json_p3/filter.rb', line 439

def self.lt?(left, right)
  left = left.first.value if left.is_a?(JSONPathNodeList) && left.length == 1
  right = right.first.value if right.is_a?(JSONPathNodeList) && right.length == 1
  return left < right if left.is_a?(String) && right.is_a?(String)
  return left < right if (left.is_a?(Integer) || left.is_a?(Float)) &&
                         (right.is_a?(Integer) || right.is_a?(Float))

  false
end

.map_iregexp(pattern) ⇒ String

Map I-Regexp pattern to Ruby regex pattern.

Parameters:

  • pattern (String)

Returns:

  • (String)


7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/json_p3/function_extensions/pattern.rb', line 7

def self.map_iregexp(pattern)
  escaped = false
  char_class = false
  mapped = String.new(encoding: "UTF-8")

  pattern.each_char do |c|
    if escaped
      mapped << c
      escaped = false
      next
    end

    case c
    when "."
      # mapped << (char_class ? c : "(?:(?![\\r\\n])\\P{Cs}|\\p{Cs}\\p{Cs})")
      mapped << (char_class ? c : "[^\\n\\r]")
    when "\\"
      escaped = true
      mapped << "\\"
    when "["
      char_class = true
      mapped << "["
    when "]"
      char_class = false
      mapped << "]"
    else
      mapped << c
    end
  end

  mapped
end

.parse_hex_digits(digits, token) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/json_p3/unescape.rb', line 81

def self.parse_hex_digits(digits, token)
  code_point = 0
  digits.each_byte do |b|
    code_point <<= 4
    case b
    when 48..57
      code_point |= b - 48
    when 65..70
      code_point |= b - 65 + 10
    when 97..102
      code_point |= b - 97 + 10
    else
      raise JSONPathSyntaxError.new("invalid escape sequence", token)
    end
  end
  code_point
end

.resolve(pointer, value, default: JSONPointer::UNDEFINED) ⇒ Object



20
21
22
# File 'lib/json_p3.rb', line 20

def self.resolve(pointer, value, default: JSONPointer::UNDEFINED)
  JSONPointer.new(pointer).resolve(value, default: default)
end

.tokenize(query) ⇒ Array<Token>

Return an array of tokens for the JSONPath expression query.

Parameters:

  • query (String)

    the JSONPath expression to tokenize.

Returns:



13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/json_p3/lexer.rb', line 13

def self.tokenize(query)
  lexer = Lexer.new(query)
  lexer.run
  tokens = lexer.tokens

  if !tokens.empty? && tokens.last.type == :token_error
    raise JSONPathSyntaxError.new(tokens.last.message || raise,
                                  tokens.last)
  end

  tokens
end

.truthy?(obj) ⇒ Boolean

Returns:

  • (Boolean)


413
414
415
416
417
418
# File 'lib/json_p3/filter.rb', line 413

def self.truthy?(obj)
  return !obj.empty? if obj.is_a?(JSONPathNodeList)
  return false if obj == :nothing

  obj != false
end

.unescape_string(value, quote, token) ⇒ String

Replace escape sequences with their equivalent Unicode code point.

Parameters:

  • value (String)
  • quote (String)

    one of '"' or "'".

  • token (Token)

Returns:

  • (String)

    A new string without escape sequences.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/json_p3/unescape.rb', line 9

def self.unescape_string(value, quote, token)
  unescaped = String.new(encoding: "UTF-8")
  index = 0
  length = value.length

  while index < length
    ch = value[index] || raise
    if ch == "\\"
      index += 1
      case value[index]
      when quote
        unescaped << quote
      when "\\"
        unescaped << "\\"
      when "/"
        unescaped << "/"
      when "b"
        unescaped << "\x08"
      when "f"
        unescaped << "\x0C"
      when "n"
        unescaped << "\n"
      when "r"
        unescaped << "\r"
      when "t"
        unescaped << "\t"
      when "u"
        code_point, index = JSONP3.decode_hex_char(value, index, token)
        unescaped << JSONP3.code_point_to_string(code_point, token)
      else
        raise JSONPathSyntaxError.new("unknown escape sequence", token)
      end
    else
      raise JSONPathSyntaxError.new("invalid character", token) if ch.ord <= 0x1F

      unescaped << ch
    end

    index += 1

  end

  unescaped
end