Class: Yajl::FFI::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/yajl/ffi/parser.rb

Overview

A streaming JSON parser that generates SAX-like events for state changes.

Examples

parser = Yajl::FFI::Parser.new
parser.key { |key| puts key }
parser.value { |value| puts value }
parser << '{"answer":'
parser << ' 42}'

Constant Summary collapse

BUF_SIZE =
4096
CONTINUE_PARSE =
1
FLOAT =
/[\.eE]/

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(&block) ⇒ Parser

Create a new parser with an optional initialization block where we can register event callbacks.

Examples

parser = Yajl::FFI::Parser.new do
  start_document { puts "start document" }
  end_document   { puts "end document" }
  start_object   { puts "start object" }
  end_object     { puts "end object" }
  start_array    { puts "start array" }
  end_array      { puts "end array" }
  key            { |k| puts "key: #{k}" }
  value          { |v| puts "value: #{v}" }
end

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/yajl/ffi/parser.rb', line 62

def initialize(&block)
  @listeners = {
    start_document: [],
    end_document: [],
    start_object: [],
    end_object: [],
    start_array: [],
    end_array: [],
    key: [],
    value: []
  }

  # Track parse stack.
  @depth = 0
  @started = false

  # Allocate native memory.
  @callbacks = callbacks
  @handle = Yajl::FFI.alloc(@callbacks.to_ptr, nil, nil)
  @handle = ::FFI::AutoPointer.new(@handle, method(:release))

  # Register any observers in the block.
  instance_eval(&block) if block_given?
end

Class Method Details

.parse(json) ⇒ Object

Parses a full JSON document from a String or an IO stream and returns the parsed object graph. For parsing small JSON documents with small memory requirements, use the json gem's faster JSON.parse method instead.

json - The String or IO containing JSON data.

Examples

Yajl::FFI::Parser.parse('{"hello": "world"}')
# => {"hello": "world"}

Raises a Yajl::FFI::ParserError if the JSON data is malformed.

Returns a Hash.


34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/yajl/ffi/parser.rb', line 34

def self.parse(json)
  stream = json.is_a?(String) ? StringIO.new(json) : json
  parser = Parser.new
  builder = Builder.new(parser)
  while (buffer = stream.read(BUF_SIZE)) != nil
    parser << buffer
  end
  parser.finish
  builder.result
ensure
  stream.close
end

Instance Method Details

#<<(data) ⇒ Object

Pass data into the parser to advance the state machine and generate callback events. This is well suited for an EventMachine receive_data loop.

data - The String of partial JSON data to parse.

Raises a Yajl::FFI::ParserError if the JSON data is malformed.

Returns nothing.


128
129
130
131
132
133
134
135
# File 'lib/yajl/ffi/parser.rb', line 128

def <<(data)
  result = Yajl::FFI.parse(@handle, data, data.bytesize)
  error(data) if result == :error
  if @started && @depth == 0
    result = Yajl::FFI.complete_parse(@handle)
    error(data) if result == :error
  end
end

#end_array(&block) ⇒ Object


107
108
109
# File 'lib/yajl/ffi/parser.rb', line 107

def end_array(&block)
  @listeners[:end_array] << block
end

#end_document(&block) ⇒ Object


91
92
93
# File 'lib/yajl/ffi/parser.rb', line 91

def end_document(&block)
  @listeners[:end_document] << block
end

#end_object(&block) ⇒ Object


99
100
101
# File 'lib/yajl/ffi/parser.rb', line 99

def end_object(&block)
  @listeners[:end_object] << block
end

#finishObject

Drain any remaining buffered characters into the parser to complete the parsing of the document.

This is only required when parsing a document containing a single numeric value, integer or float. The parser has no other way to detect when it should no longer expect additional characters with which to complete the parse, so it must be signaled by a call to this method.

If you're parsing more typical object or array documents, there's no need to call `finish` because the parse will complete when the final closing `]` or `}` character is scanned.

Raises a Yajl::FFI::ParserError if the JSON data is malformed.

Returns nothing.


153
154
155
156
# File 'lib/yajl/ffi/parser.rb', line 153

def finish
  result = Yajl::FFI.complete_parse(@handle)
  error('') if result == :error
end

#key(&block) ⇒ Object


111
112
113
# File 'lib/yajl/ffi/parser.rb', line 111

def key(&block)
  @listeners[:key] << block
end

#start_array(&block) ⇒ Object


103
104
105
# File 'lib/yajl/ffi/parser.rb', line 103

def start_array(&block)
  @listeners[:start_array] << block
end

#start_document(&block) ⇒ Object


87
88
89
# File 'lib/yajl/ffi/parser.rb', line 87

def start_document(&block)
  @listeners[:start_document] << block
end

#start_object(&block) ⇒ Object


95
96
97
# File 'lib/yajl/ffi/parser.rb', line 95

def start_object(&block)
  @listeners[:start_object] << block
end

#value(&block) ⇒ Object


115
116
117
# File 'lib/yajl/ffi/parser.rb', line 115

def value(&block)
  @listeners[:value] << block
end