Инкрементальный разбор JSON в C # - PullRequest
11 голосов
/ 27 января 2012

Я пытаюсь выполнить синтаксический анализ JSON, т.е. на основе условия.

Ниже мое сообщение json, и в настоящее время я использую JavaScriptSerializer для десериализации сообщения.

string json = @"{"id":2,
"method":"add",
"params":
   {"object":
       {"name":"test"
        "id":"1"},
        "position":"1"}
  }";

JavaScriptSerializer js = new JavaScriptSerializer();
Message m = js.Deserialize<Message>(json);

Класс сообщения показан ниже:

 public class Message
 {
        public string id { get; set; }
        public string method { get; set; }
        public Params @params { get; set; }
        public string position { get; set; }
 }
public class Params
{
        public string name { get; set; }
        public string id{ get; set; 
}

Приведенный выше код анализирует сообщение без проблем.Но он анализирует весь JSON одновременно.Я хочу, чтобы он продолжал синтаксический анализ, только если значение параметра "method" равно "add".Если это не «добавить», то я не хочу, чтобы он продолжал анализ остальной части сообщения.Есть ли способ сделать инкрементальный анализ на основе условия в C #?(Среда: VS 2008 с .Net 3.5)

Ответы [ 6 ]

14 голосов
/ 27 января 2012

Я должен признать, что я не так хорошо знаком с JavaScriptSerializer, но если вы готовы использовать JSON.net , у него есть JsonReader, который действует очень как DataReader.

using(var jsonReader = new JsonTextReader(myTextReader)){
  while(jsonReader.Read()){
    //evaluate the current node and whether it's the name you want
    if(jsonReader.TokenType.PropertyName=="add"){
      //do what you want
    } else {
      //break out of loop.
    }
  }
}
9 голосов
/ 09 июня 2014

Вот общие и простые методы, которые я использую для анализа, загрузки и создания очень больших файлов JSON.В коде теперь используется стандартная библиотека JSON.Net .К сожалению, документация не очень ясна о том, как это сделать, но это не очень трудно понять.и наоборот.Мы хотим поддерживать очень большие файлы, размер которых ограничен только вашим устройством хранения (не памятью).Поэтому при сериализации метод принимает IEnumerable<T> и при десериализации возвращает то же самое.Таким образом, вы можете обработать весь файл, не ограничиваясь памятью.

Я использовал этот код для файлов размером несколько ГБ с разумной производительностью.

//Serialize sequence of objects as JSON array in to a specified file
public static void SerializeSequenceToJson<T>(this IEnumerable<T> sequence, string fileName)
{
    using (var fileStream = File.CreateText(fileName))
        SerializeSequenceToJson(sequence, fileStream);
}

//Deserialize specified file in to IEnumerable assuming it has array of JSON objects
public static IEnumerable<T> DeserializeSequenceFromJson<T>(string fileName)
{
    using (var fileStream = File.OpenText(fileName))
        foreach (var responseJson in DeserializeSequenceFromJson<T>(fileStream))
            yield return responseJson;
}

//Utility methods to operate on streams instead of file
public static void SerializeSequenceToJson<T>(this IEnumerable<T> sequence, TextWriter writeStream, Action<T, long> progress = null)
{
    using (var writer = new JsonTextWriter(writeStream))
    {
        var serializer = new JsonSerializer();
        writer.WriteStartArray();
        long index = 0;
        foreach (var item in sequence)
        {
            if (progress != null)
                progress(item, index++);

            serializer.Serialize(writer, item);
        }
        writer.WriteEnd();
    }
}
public static IEnumerable<T> DeserializeSequenceFromJson<T>(TextReader readerStream)
{
    using (var reader = new JsonTextReader(readerStream))
    {
        var serializer = new JsonSerializer();
        if (!reader.Read() || reader.TokenType != JsonToken.StartArray)
            throw new Exception("Expected start of array in the deserialized json string");

        while (reader.Read())
        {
            if (reader.TokenType == JsonToken.EndArray) break;
            var item = serializer.Deserialize<T>(reader);
            yield return item;
        }
    }
}
5 голосов
/ 27 января 2012

Если взглянуть на Json.NET , он предоставляет не кэшируемый анализатор JSON, предназначенный только для пересылки, который удовлетворит ваши потребности.

См. Класс JsonReader и JsonTextReader в документации .

2 голосов
/ 23 июня 2017

Сейчас я нахожусь на третьем часе неизвестного промежутка времени, наблюдая за десериализацией 160 ГБ JSON в объекты класса.Мое использование памяти зависло на ~ 350 МБ, и когда я проверяю объекты памяти, это все, что GC может позаботиться.Вот что я сделал:

    FileStream fs = File.Open("F:\\Data\\mysuperbig150GB.json", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
    StreamReader sr = new StreamReader(fs);

    using (JsonReader reader = new JsonTextReader(sr))
    {
        JsonSerializer serializer = new JsonSerializer();

        MyJsonToClass result = serializer.Deserialize<MyJsonToClass>(reader);
    }

Проблема в десериализации.Эти 160 ГБ данных намного больше, чем тот, который может обрабатывать мой компьютер за один раз.

  1. Я использовал небольшой фрагмент кода (что сложно, даже просто открывая файл 160 ГБ) и получил структуру классовvia jsontochsarp .

  2. Я создал специальный класс для большой коллекции в структуре классов автоматически сгенерированного через json-tool и создал подкласс System.Collection.ObjectModel.ObservableCollection вместо списка.Они оба реализуют IEnumberable, что, как мне кажется, является всем, что заботится о десериализаторе JSON Newtsonsoft.

  3. Я вошел и переопределил InsertItem, например:*

    Опять же, мои проблемы частично касались скорости десериализации JSON, но помимо этого я не смог уложить ~ 160 ГБ данных JSON в сбор.Даже если уж точно сказать, это было бы в десятках рабочих мест, намного больше, чем то, чем будет рад .net.

    InsertItem в ObservableCollection - единственный метод, который мне известен, который вы можете обрабатывать, когдапроисходит десериализация.List.Add () нет.Я знаю, что это решение не "элегантно", но оно работает, когда я его набираю.

1 голос
/ 27 января 2012

Вам понадобится парсер SAX-типа для JSON

http://en.wikipedia.org/wiki/Simple_API_for_XML

http://www.saxproject.org/event.html

SAX вызывает событие при разборе каждого фрагмента документа.

Выполнение чего-либо подобного в JSON будет (должно) быть довольно простым, учитывая, насколько прост синтаксис JSON.

Этот вопрос может быть полезен: Существует ли потоковый API для JSON?

И еще одна ссылка: https://www.p6r.com/articles/2008/05/22/a-sax-like-parser-for-json/

0 голосов
/ 27 января 2012

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

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

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