Как написать основной класс разбора JSON

Может ли кто-нибудь посоветовать написать класс, который возьмет данные JSON, и попытается проанализировать его в простой буферный список, из которого мы могли бы прочитать данные?

Ex. JSON

{ name: 'John', age: 56 } 

.. будет разбираться в таблицу пар значений значения

name John
age  56

Как написать метод анализа, который поможет создать более быстрый и простой?

Просьба не предлагать какую-либо существующую библиотеку. Предоставьте концепцию для разбора JSON.

Ответ 1

Этот ответ предполагает, что вы действительно хотите написать парсер и готовы приложить необходимые усилия.

Вы должны начать с формальной спецификации JSON. Я нашел http://www.ietf.org/rfc/rfc4627.txt. Это точно определяет язык. Вы ДОЛЖНЫ внедрять все в спецификации и писать тесты для этого. Ваш парсер ДОЛЖЕН обслуживать неправильные JSON (например, ваши) и выбрасывать исключения.

Если вы хотите написать парсер, остановитесь, подумайте, а затем не делайте этого. Это большая работа, чтобы заставить ее работать правильно. Независимо от того, что вы делаете, выполняйте надлежащую работу - неполные парсеры являются угрозой и никогда не должны распространяться.

Вы ДОЛЖНЫ писать код, который соответствует. Вот некоторые фразы из спецификации. Если вы их не понимаете, вам придется тщательно изучить и убедиться, что вы понимаете:

"Текст JSON будет закодирован в Unicode. Кодировка по умолчанию UTF-8".

"Парсер JSON ДОЛЖЕН принимать все тексты, соответствующие JSON грамматика".

"Кодирование: 8 бит, если UTF-8; двоичный, если UTF-16 или UTF-32

  JSON may be represented using UTF-8, UTF-16, or UTF-32.  When JSON
  is written in UTF-8, JSON is 8bit compatible.  When JSON is
  written in UTF-16 or UTF-32, the binary content-transfer-encoding
  must be used.

"

"Любой символ может быть экранирован. Если символ находится в Basic Многоязычная плоскость (U + 0000 через U + FFFF), то это может быть представленный как шестисимвольная последовательность: обратный солидус, после которого следуют строчной буквой u, за которой следуют четыре шестнадцатеричных цифры, которые закодируйте точку кода символа. Шестнадцатеричные буквы A, хотя и F может быть верхним или строчным. Так, например, строка, содержащая только один обратный символ солидуса может быть представлен как" \ U005C "."

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

Если вы все еще заинтересованы, вам следует серьезно подумать об использовании генератора парсера. Примерами являются JAVACC, CUP и мой предпочтительный инструмент ANTLR. ANTLR является очень мощным, но с ним может быть трудно начать. См. Также предложение Parboiled, которое я бы рекомендовал. JSON относительно прост, и это было бы полезным упражнением. Большинство генераторов парсеров генерируют полный синтаксический анализатор, который может создавать исполняемый код или генерировать дерево разбора вашего JSON.

Существует синтаксический анализатор JSON, использующий ANTLR в http://www.antlr.org/wiki/display/ANTLR3/JSON+Interpreter, если вам разрешено заглядывать в него. Я также только что обнаружил Parboiled parser-generator для JSON. Если ваша основная причина написания парсера - это узнать, как это сделать, это, вероятно, хорошая отправная точка.

Если вам не разрешено (или не нужно) использовать синтаксический анализатор, вам придется создать свой собственный синтаксический анализатор. Обычно это происходит в двух частях:

лексер/токенизатор. Это распознает основные примитивы, определенные в спецификации языка. В этом случае он должен будет распознать фигурные скобки, кавычки и т.д. Возможно, это также создаст представление чисел.

the AbstractSyntaxTree (http://en.wikipedia.org/wiki/Abstract_syntax_tree, AST) генератор. Здесь вы пишете код для сборки дерева, представляющего абстракцию вашего JSON (например, пробелы и завитки отбрасываются).

Когда у вас есть АСТ, нужно легко перебирать узлы и создавать желаемый результат.

Но писать синтаксические анализаторы, даже для простого языка, такого как JSON, - это много работы.

Ответ 2

Если ваш "JSON" действительно такой, вы должны сначала взять бейсбольную биту и поразить ее продюсера. Шутки в сторону.

Если вы действительно настаиваете на написании своего собственного класса (почему?), Вы можете, например, использовать такой интерфейс:

public interface MyParser
{
    boolean parse()
        throws MyParsingException;
    MyParser next();
}

Реализации будут тогда принимать CharBuffer в качестве аргумента и класс построителя карты; и для разбора вы бы сделали:

final CharBuffer buf = CharBuffer.wrap(yourSource);
final MyMapBuilder builder = new MyMapBuilder();

MyParser parser = new OpenBracketParser(buf, builder);

while (parser.parse())
    parser = parser.next();

// result is builer.build()

Это всего лишь один пример...

Второе решение, вы хотите использовать существующий инструмент разбора; в этом случае взгляните на Parboiled. Гораздо проще в использовании, чем antlr, jflex или другие, так как вы пишете свои грамматики на чистом Java.

Наконец, если вы решите, что этого достаточно, и решите использовать библиотеку JSON (вам действительно следует это сделать), воспользуйтесь Джексоном, который может читать даже такой искаженный JSON:

public static void main(final String... args)
    throws IOException
{
    final ObjectMapper mapper = new ObjectMapper()
        .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
        .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);

    final JsonNode node = mapper.readTree("{name: 'John'}");
    System.out.println(node); // {"name":"John"}
}

Ответ 3

Я написал один раньше. шаги:

  1. Возьмите строку, представляющую текст JSON.

  2. Создайте класс JsonToken. Я называю мой JToken.

  3. Перейдите весь текст из шага # 1 и разберите JToken (s).

  4. Рекурсивно сгруппируйте и вложите свой JToken (s).

  5. Попытка сохранить его простым и единообразным. Все узлы JToken имеют дочерний массив, который может иметь 0 или более дочерних элементов. Если узел является массивом, отметьте как массив. Дочерний массив используется для дочерних элементов узла, если is является OBJECT или ARRAY. Единственное, что меняется, это то, что он помечен как. Также сохраняйте все значения как строковый тип. Таким образом, вам нужен только один элемент на узле с именем "value", который можно интерпретировать как правильный тип данных после выполнения всей тяжелой работы.

  6. Используйте защитное кодирование и юнит-тесты. Написать тесты для всех компонентов парсера. Лучше потратить дополнительные 3 часа на написание кода параноидальным образом, когда вы предполагаете, что совершаете ошибки каждую секунду, чем на то, чтобы тратить 3 часа на поиск ошибок. Код достаточно параноидальный, и вы очень редко будете тратить время на разочарование при отладке.

Пример кода: когда я делал легкий (иронически) вызов на code-eval.com. Был вызов разбора меню JSON. Я думал, что было бы обманом использовать любые встроенные функции, потому что для меня весь смысл проблем с кодом заключается в проверке ваших возможностей по решению проблем алгоритма. Задача здесь: https://www.codeeval.com/open_challenges/102/

Мой код, который проходит эту задачу, используя синтаксический анализатор, созданный с нуля в JavaScript:

CODE: https://pastebin.com/BReK9iij
Was not able to post it on stack-overflow because it is too much code.
Put it in a non-expiring paste-bin post.

Примечание: этот код может использовать некоторые улучшения. Некоторые из них очень неэффективны и не будут работать с Unicode.

Я бы не рекомендовал писать свой собственный анализатор JSON, если вы не интерпретируете JSON каким-то нестандартным способом.

Например: в настоящее время я использую JSONedit для организации веток для текстового приключения. Я использую только формат файла JSON, потому что он компактен, а программа просмотра позволяет мне расширять и сокращать элементы. Стандартный синтаксический анализатор, который поставляется с GOLang, не интерпретирует информацию так, как я хочу, чтобы она интерпретировалась, поэтому я пишу свой собственный анализатор.

Ответ 4

public abstract class AbstractMessageObject {
  public String toString() {
    Gson gson = new Gson();
    //here mapping Object class name is prefixed to json message so otherwise knows the mapping object
    return "^" + this.getClass().getName() + "^" + gson.toJson(this);
  }
}

Вы можете создать любой bean, расширив этот AbstractMessageObject. Всякий раз, когда вы хотите проанализировать этот объект на json, вам нужно только вызвать метод toString

Ответ 5

Я написал простой парсер на Kotlin, он не полный. Тем не менее, он может работать в качестве отправной точки для вашей собственной реализации.

Спасибо @peter.murray.rust за предоставленные идеи, а также вдохновлен парсером h2database.

Он в основном считывает строковые символы JSON (используя StringReader) и пытается проанализировать их на основе ожидаемых токенов JSON. Он реализует функцию чтения токенов, непрочитанных символов и, в конечном итоге, преобразования результата в AST.

Вы можете найти код и контрольные примеры на github.