Working from these assumptions:
- for some reason, you can't simply use Ruby's stdlib JSON library, as suggested by many others
- the
"B" strings in your incoming data may be replaced by any other single valid JSON object, however complex; e.g. { "key1": ["ABC", { "key2": "{\"key3\": [\"DEF\"]}" }]}
- you want only and exactly the second values (always integers) of arrays nested one level deep in a top-level array; e.g.
[[..., 123], [..., 456]] yields [123, 456]
Here is your function:
def parse_integers_from_specific_JSON (string)
ary_depth = 0 # Level of nesting within arrays
obj_depth = 0 # Level of nesting within objects
in_string = false # Flag for whether the parser is inside a string
escaped = false # Flag for whether the next character will be escaped
int_buffer = "" # String buffer for integer digits
integers = [] # Result array of integers
string.each_char do |c|
if (in_string) && !(escaped) && (c == '\\')
# When an escape character is found in a string, set the 'escaped' flag
escaped = true
next
end
if (c == '"')
if !in_string
# When a quote is found outside a string, enter the new string
in_string = true
elsif escaped
# When an escaped quote is found within a string, ignore it
else
# When an unescaped quote is found within a string, exit the string
in_string = false
end
end
# If relevant, the 'escaped' flag has escaped its quote by now; unset it
escaped = false
if !(in_string) && (c =~ /\d/) && (obj_depth == 0) && (ary_depth == 2)
# If a digit is encountered at the correctly nested position,
# append it to the integer buffer string
int_buffer << c
elsif !(int_buffer.empty?)
# If the integer buffer string has digits in it and the current character
# is not a digit, flush the contents of the buffer into the result array
integers << int_buffer.to_i
int_buffer.clear
end
unless in_string
# Check for unquoted special characters and adjust depth
case c
when '{' then obj_depth += 1
when '}' then obj_depth -= 1
when '[' then ary_depth += 1
when ']' then ary_depth -= 1
end
end
end
return integers
end
Here's a snippet showing it in action, using a beefed-up version of your sample data:
incoming_data_string = (<<-JSON).strip
[
["B",90461],
[[],85547],
[["A"],85709],
["",85029],
[[123,456],85487],
[[456,[789],"A"],83419],
[{ a: [123] },83411],
["B",85311],
["B",81335],
["B",85149],
[{ "key1": ["ABC", { "key2": "{\\"key3\\": [\\"DEF\\"]}" }]},85959]
]
JSON
p parse_integers_from_specific_JSON(incoming_data_string)
#=> [90461, 85547, 85709, 85029, 85487, 83419, 83411, 85311, 81335, 85149, 85959]
This won't be particularly fast. If you want it to be fast, the correct answer is to require 'json' and use JSON::parse.
JSONobject, can you please post the right thingrequire "json"and theJSONmodule are available in Ruby standard lib (no gems need be installed)?