Эффективный способ десериализации одного и того же потока json дважды с помощью Newtonsoft - PullRequest
2 голосов
/ 20 марта 2020

У меня есть json, который я получаю в System.IO.StreamReader. Его структура выглядит следующим образом:

{
    "data": [
        {
            "Property1": "Value1",
            "Property2": "Value2"
        },
        {
            "Property1": "Value3",
            "Property2": "Value4"
        }
    ],
    "metaData": {
        (some other structure)
    }
}

И у меня есть два класса, один из которых имеет Property1 в качестве поля данных, а другой - Property2 в качестве поля данных.

Я хотел бы использовать входящий поток, чтобы десериализовать этот json в две отдельные коллекции из двух имеющихся у меня классов.

До сих пор мне удавалось сделать это, только сначала упорядочив поток, а затем вызвав его. DeserliazeObject(string) дважды. Но тогда огромный кусок памяти выделяется излишне.

Как я могу сделать это с помощью одного вызова DeserializeObject(streamReader)? Предоставляет ли Newtonsoft какие-либо настройки сериализации, которые могут мне помочь в этом?

Это мой код, который неэффективен, поскольку я преобразую поток в строку перед вызовом десериализатора:

var responseContent = await response.Content.ReadAsStringAsync();
var jObject = JObject.Parse(responseContent);
var records = GetRecords(jObject);

private IList<(IClass1, IClass2)> GetRecords(JObject jObject)
{
  return jObject["data"]
    .Children()
    .Select(CreateRecord)
    .ToList();
}

private (Class1, Class2) CreateRecord(JToken recordToken)
{
  var object1 = recordToken.ToObject<IClass1>();
  var object2 = recordToken.ToObject<IClass2>();
  return (object1, object2);
}

1 Ответ

2 голосов
/ 20 марта 2020

Я предлагаю вам 3 пути, начиная с наименее эффективного, но с наименьшим кодом.

десериализация в объект JO непосредственно из Stream

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

var serializer = new JsonSerializer();
using (var textReader = new StreamReader(stream))
using (var jsonReader = new JsonTextReader(textReader))
{
    var jobject = (JObject)serializer.Deserialize(jsonReader);
}

Десериализуйте в класс C#, затем создайте нужный вам класс. JObject имеет больший вес, чем POCO, и потребляет больше памяти.

Это просто. Предположим, что созданный вами класс Data.

var serializer = new JsonSerializer();
using (var textReader = new StreamReader(stream))
using (var jsonReader = new JsonTextReader(textReader))
{
    var data = serializer.Deserialize<Data>(jsonReader);
}

Используйте JsonTextReader для непосредственного считывания в целевую структуру данных

Это предполагает ручное считывание свойств и значений вашего потока json в структуру тебе нужно. Это экономит пространство памяти любых промежуточных объектов (точнее, любые промежуточные объекты будут содержать только текущий токен, который вы читаете, а не все данные). Если вы вернете IEnumerable<(IClass1, IClass2)>, пользователь вашего метода может фактически обработать результат в потоковом режиме, сделав это алгоритмом online .

Я оставлю вас копаться в документации, чтобы узнайте, как его использовать:)

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