2015-12-04 2 views
1

ПроблемаСинтаксические огромный объект JSON с помощью объекта в Рубине

У меня есть файл в формате JSON, состоящий из огромного множества мелких объектов JSon. Теперь, если я попытаюсь разобрать его обычными средствами, прочитайте файл в памяти и затем вызовите любой синтаксический анализ json (например, json.parse или Oj.parse), он будет потреблять всю доступную мне память системы и не закончит.

Что я хочу?

Определенный способ разобрать его по потоку, и каждый раз, когда он заканчивает объект, он обращается к функции с объектом. С этим я считаю, что использование памяти будет очень низким и постоянным.

Что я достиг до сих пор

Я проверил два драгоценных камней (yajl и json-stream) и нашел следующее решение с использованием yajl:

def post_init 
    @parser = Yajl::Parser.new(:symbolize_keys => true) 
end 

def object_parsed(obj) 
    puts "Sometimes one pays most for the things one gets for nothing. - Albert Einstein" 
    puts obj.inspect 
end 

def connection_completed 
    # once a full JSON object has been parsed from the stream 
    # object_parsed will be called, and passed the constructed object 
    @parser.on_parse_complete = method(:object_parsed) 
end 

# Parse itself 
post_init 
connection_complete 
@parse << File.read("data.json",2048) 

Но есть еще проблема с этим подходом, @ parser.on_parse_complete запускается только после закрытия массива (таким образом, после завершения обработки json). Но с другой стороны, если я отформатирую json с объектом на строку, он отлично работает, и функция object_parsed вызывается дважды, один раз в строке.

Json образец:

[ 
    { 
    "v": { 
     "M0": 2 
    }, 
    "dims": { 
     "D371665580_86": "M77", 
     "D2088848381_86": "M5", 
     "D372510617_86": "M42" 
    } 
    }, 
    { 
    "v": { 
     "M0": 2 
    }, 
    "dims": { 
     "D371665580_86": "M77", 
     "D2088848381_86": "M5", 
     "D372510617_86": "M42" 
    } 
    } 
] 

ответ

0

Я хотел бы забыть о правилах и идти со следующим подходом:

#!/usr/bin/env ruby 

require 'stringio' # for tests 

input = '[{"menu": { 
      "id": "file", 
      "value": "File" 
      } 
    }, 
    {"menu": { 
      "id": "file2", 
      "value": "File2" 
      } 
    }]' 

io = StringIO.new input # here a file stream is opened 

loop.inject(counter: 0, string: '') do |acc| 
    char = io.getc 

    break if char.nil? # EOF 
    next acc if acc[:counter].zero? && char != '{' # between objects 

    acc[:string] << char 

    if char == '}' && (acc[:counter] -= 1).zero? 
    # ⇓⇓⇓ # CALLBACK, feel free to JSON.parse here 
    puts acc[:string].gsub(/\p{Space}+/, ' ') 
    next {counter: 0, string: ''} # from scratch 
    end 

    acc.tap do |result| 
    result[:counter] += 1 if char == '{' 
    end 
end 

#⇒ {"menu": { "id": "file", "value": "File" } } 
# {"menu": { "id": "file2", "value": "File2" } } 

Здесь мы просто читать поток байт за байтом и как только очень закрывается фигурная скобка, мы испускаем puts. Это эффективный и пуленепробиваемый, предполагая, что вы обеспокоены вводом массива, состоящего из хэшей.

Надеюсь, это поможет.

+0

Uh. Это не является «пуленепробиваемым». http://ideone.com/cS8TvJ –

+0

@ Джордан, что не так с примером, который вы предоставили? Речь идет о обратном вызове, чтобы решить, что делать со вредоносными данными. Вы ожидаете, что любой синтаксический анализатор json сделает что-то значимое с этим типом ввода? – mudasobwa

+0

Вход в мой код не вредоносных данных, это 100% действительный JSON. Любой корректный парсер JSON отлично справится с этим. –

Смежные вопросы