1 2 3 4 5 6 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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
|
require_relative '../../../puppet/concurrent/thread_local_singleton'
module Puppet::Pops
module Serialization
module JsonPath
# Creates a _json_path_ reference from the given `path` argument
#
# @path path [Array<Integer,String>] An array of integers and strings
# @return [String] the created json_path
#
# @api private
def self.to_json_path(path)
p = '$'
path.each do |seg|
if seg.nil?
p << '[null]'
elsif Types::PScalarDataType::DEFAULT.instance?(seg)
p << '[' << Types::StringConverter.singleton.convert(seg, '%p') << ']'
else
# Unable to construct json path from complex segments
return nil
end
end
p
end
# Resolver for JSON path that uses the Puppet parser to create the AST. The path must start
# with '$' which denotes the value that is passed into the parser. This parser can easily
# be extended with more elaborate resolution mechanisms involving document sets.
#
# The parser is limited to constructs generated by the {JsonPath#to_json_path}
# method.
#
# @api private
class Resolver
extend Puppet::Concurrent::ThreadLocalSingleton
def initialize
@parser = Parser::Parser.new
@visitor = Visitor.new(nil, 'resolve', 2, 2)
end
# Resolve the given _path_ in the given _context_.
# @param context [Object] the context used for resolution
# @param path [String] the json path
# @return [Object] the resolved value
#
def resolve(context, path)
factory = @parser.parse_string(path)
v = resolve_any(factory.model.body, context, path)
v.is_a?(Builder) ? v.resolve : v
end
def resolve_any(ast, context, path)
@visitor.visit_this_2(self, ast, context, path)
end
def resolve_AccessExpression(ast, context, path)
bad_json_path(path) unless ast.keys.size == 1
receiver = resolve_any(ast.left_expr, context, path)
key = resolve_any(ast.keys[0], context, path)
if receiver.is_a?(Types::PuppetObject)
PCORE_TYPE_KEY == key ? receiver._pcore_type : receiver.send(key)
else
receiver[key]
end
end
def resolve_NamedAccessExpression(ast, context, path)
receiver = resolve_any(ast.left_expr, context, path)
key = resolve_any(ast.right_expr, context, path)
if receiver.is_a?(Types::PuppetObject)
PCORE_TYPE_KEY == key ? receiver._pcore_type : receiver.send(key)
else
receiver[key]
end
end
def resolve_QualifiedName(ast, _, _)
v = ast.value
'null' == v ? nil : v
end
def resolve_QualifiedReference(ast, _, _)
v = ast.cased_value
'null'.casecmp(v) == 0 ? nil : v
end
def resolve_ReservedWord(ast, _, _)
ast.word
end
def resolve_LiteralUndef(_, _, _)
'undef'
end
def resolve_LiteralDefault(_, _, _)
'default'
end
def resolve_VariableExpression(ast, context, path)
# A single '$' means root, i.e. the context.
bad_json_path(path) unless EMPTY_STRING == resolve_any(ast.expr, context, path)
context
end
def resolve_CallMethodExpression(ast, context, path)
bad_json_path(path) unless ast.arguments.empty?
resolve_any(ast.functor_expr, context, path)
end
def resolve_LiteralValue(ast, _, _)
ast.value
end
def resolve_Object(ast, _, path)
bad_json_path(path)
end
def bad_json_path(path)
raise SerializationError, _('Unable to parse jsonpath "%{path}"') % { :path => path }
end
private :bad_json_path
end
end
end
end
|