【问题标题】:Serializing an array of objects one at a time with JSON.NET使用 JSON.NET 一次序列化一组对象
【发布时间】:2023-03-08 23:15:02
【问题描述】:

我尝试将多个项目序列化为 json,并将它们格式化为数组。

它是基于事件的,因为它有很多数据不能作为一个整体保存在内存中。但我需要将每个项目序列化为一个文件并将其格式化为数组。

_jsonWriter = new JsonTextWriter(new StreamWriter("Output.json")));
DataGatherer.ItemGathered += item =>
{
    _jsonSerializer.Serialize(_jsonWriter, item);
    _jsonWriter.Flush();
};

目前输出如下:

{
  "Id": 218515,
  "Name": "A"
}{
  "Id": 118647,
  "Name": "B"
}

由于序列化程序将每个项目序列化为一个对象,因此并不知道它是一个数组。

那么我如何告诉 JSON.Net 序列化程序将每个项目作为数组的一个项目来处理,并像这样格式化数据:

[{ "Id": 218515, "Name": "A"},{"Id": 118647,"Name": "B"}]

感谢任何提示!

【问题讨论】:

    标签: c# javascript .net json json.net


    【解决方案1】:

    我会创建一个简单的JsonItemWriter 类来包装JsonTextWriter。该类只需要跟踪是否有任何项目已写入输出。如果没有,请在写入项目之前使用内部 JsonTextWriter 将 StartArray 写入流。当外部 ItemWriter 关闭时,将 EndArray 写入流。然后更改您的事件处理程序以使用 ItemWriter 而不是 JsonTextWriter。

    这是我对JsonItemWriter 的想法:

    class JsonItemWriter
    {
        private JsonTextWriter innerWriter;
        private JsonSerializer serializer;
    
        public JsonItemWriter(JsonTextWriter innerWriter, JsonSerializer serializer)
        {
            this.innerWriter = innerWriter;
            this.serializer = serializer;
        }
    
        public void WriteItem(object item)
        {
            if (innerWriter.WriteState == Newtonsoft.Json.WriteState.Start)
            {
                innerWriter.WriteStartArray();
            }
            serializer.Serialize(innerWriter, item);
            innerWriter.Flush();
        }
    
        public void Close()
        {
            innerWriter.WriteEndArray();
            innerWriter.Close();
        }
    }
    

    然后像这样设置您的事件处理程序:

    _jsonWriter = new JsonTextWriter(new StreamWriter("Output.json"));
    _itemWriter = new JsonItemWriter(_jsonWriter, _jsonSerializer);
    
    DataGatherer.ItemGathered += item =>
    {
        _itemWriter.WriteItem(item);
    };
    

    这是一个使用模拟 DataGatherer 代替您的简短演示:

    class Program
    {
        static void Main(string[] args)
        {
            JsonSerializer jsonSerializer = new JsonSerializer();
            JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter("Output.json"));
            JsonItemWriter itemWriter = new JsonItemWriter(jsonWriter, jsonSerializer);
    
            MockDataGatherer gatherer = new MockDataGatherer();
            gatherer.ItemGathered += item =>
            {
                itemWriter.WriteItem(item);
            };
    
            var items = new[]
            {
                new { Id = 218515, Name = "A" },
                new { Id = 118647, Name = "B" }
            };
            gatherer.SimulateReceivingItems(items);
    
            itemWriter.Close();
    
            using (StreamReader reader = new StreamReader("Output.json"))
            {
                Console.WriteLine(reader.ReadToEnd());
            }
        }
    }
    
    class MockDataGatherer
    {
        public void SimulateReceivingItems(IEnumerable<object> items)
        {
            foreach (object item in items)
            {
                ItemGathered(item);
            }
        }
    
        public event ItemGatheredEventHandler ItemGathered;
        public delegate void ItemGatheredEventHandler(object item);
    }
    

    这是上面的输出(注意对象现在被包装在一个数组中):

    [{"Id":218515,"Name":"A"},{"Id":118647,"Name":"B"}]
    

    【讨论】:

    • 非常感谢!我得到的最详细的答案和代码示例 :) 效果很好而且很简单,尽管我认为 Json.net 提供了内置的方法来做到这一点。
    • AFAIK,Json.Net 只提供内置的方法来完成常见的任务。如果您可以一次将所有项目保存在内存中,则可以简单地将它们添加到列表中,并在完成收集后将其序列化。但是一旦你的项目太多以至于无法记忆,你就必须发挥创造力并想出不同的方法(因此有这个解决方案)。不过我很高兴它对你有用!
    【解决方案2】:

    你为什么不建立一个IEnumerable&lt;YourObject&gt;。 它不需要加载内存中的所有集合,当您将其提供给序列化程序时,它会被解析为数组。 一种简单的方法是将您的 ItemGathered 事件调用替换为一个简单的 yield return 像你一样

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-11
      • 1970-01-01
      相关资源
      最近更新 更多