【问题标题】:How do I use the PipeReader for reading a JSON flatfile?如何使用 PipeReader 读取 JSON 平面文件?
【发布时间】:2021-02-12 13:45:40
【问题描述】:

我正在尝试学习这个新的 system.io.pipelines,以及用于反序列化 json 的新 webapi 策略...

我编写了自己的 JsonConverter,但我无法找出从 json 平面文件夹具初始化 Utf9JsonReader 的正确方法。

这是测试:

    [Fact]
    public void WhenGivenJsonObjectThenEntityDTOReturned() {
                    
        using(var stream = new FileStream("Fixtures/BookStoreJson.json", FileMode.Open))
        {
            var pipe = PipeReader.Create(stream);
            ReadResult bytes;
            pipe.TryRead(out bytes);
            var reader = new Utf8JsonReader(bytes.Buffer);
            var target = new EntityDTOConverter();
            reader.Read();
            var actual = target.Read(ref reader, typeof(EntityDTO), new JsonSerializerOptions());
            
            Assert.True(actual.Props.ContainsKey("name"));
        }
    

    }

当我调试它时,bytes.buffer 设置为 0 字节,即使 BookStoreJson.json 文件包含以下内容:

{
    "name": "Tattered Cover",
    "store":{
       "book":[
          {
             "category":"reference",
             "author":"Nigel Rees",
             "title":"Sayings of the Century",
             "price":8.95
          },
          {
             "category":"fiction",
             "author":"Evelyn Waugh",
             "title":"Sword of Honour",
             "price":12.99
          },
          {
             "category":"fiction",
             "author":"J. R. R. Tolkien",
             "title":"The Lord of the Rings",
             "isbn":"0-395-19395-8",
             "price":22.99
          }
       ],
       "bicycle":{
          "color":"red",
          "price":19.95
       }
    }
 }

【问题讨论】:

    标签: c# system.io.pipelines


    【解决方案1】:

    抱歉,我没有意识到异步进程,我正在从控制台应用程序进行测试。除了不确定这个答案对您有多大帮助之外,基本上您可以通过访问结果来同步运行异步任务。缓冲区大小也有限制,如果 json 文件大小较大,您可能需要创建自定义池并使用 AdvanceTo 选项来读取缓冲区末尾以获取流。

    using System;
    using System.Buffers;
    using System.IO;
    using System.IO.Pipelines;
    using System.Text;
    using System.Text.Json;
    using System.Threading.Tasks;
    using Xunit;
     
    
                public class Person
                {
                    public string Email { get; set; }
                    public bool Active { get; set; }
                    public string CreatedDate { get; set; }
                    public string[] Roles { get; set; }
                }
        
                [Fact]
                public void WhenGivenJsonObjectThenEntityDTOReturned()
                {
                    //{
                    //    "Email": "james@example.com",
                    //    "Active": true,
                    //    "CreatedDate": "2013-01-20T00: 00: 00Z",
                    //    "Roles": [
                    //    "User",
                    //    "Admin"
                    //    ]
                    //}
        
                    using (var stream = new FileStream(@"c:\temp\json.json", FileMode.Open))
                    {
                        var reader = PipeReader.Create(stream, 
                            new StreamPipeReaderOptions(bufferSize:4096 *2));
        
                        ValueTask<ReadResult> readResult = reader.ReadAsync();
                        ReadOnlySequence<byte> buffer = readResult.Result.Buffer;
                        Assert.True(readResult.IsCompleted);
                        var jsonStreamReader = new Utf8JsonReader(buffer);
                        var expectedJson = JsonSerializer
                            .Deserialize<Person>(Encoding.UTF8.GetString(buffer.ToArray()));
                        Assert.Equal("james@example.com", expectedJson.Email);
                    }
                }
    

    【讨论】:

    • readResult 未定义,仅用作输出变量...这是 C# 的新事物吗?今晚我会试一试!
    • 这个 sn-p 不起作用:1) 必须将任务标记为 public async task 才能使用 await pipe.ReadAsync 2) 错误 CS4012:不能在异步中声明类型为“Utf8JsonReader”的参数或局部变量方法或 lambda 表达式。我什至不知道编译器会以这种方式失败,但是这个和问题 #3 导致上面的答案无法编译。 3)变量readResult没有定义……这三个问题让我无法接受这个答案……
    【解决方案2】:

    这样就可以通过 PipeReader 读取文件了-

                using (var stream = new FileStream(@"test.json", FileMode.Open))
                {
                    var pipeReader = System.IO.Pipelines.PipeReader.Create(stream);
    
                    while (true)
                    {
                        var pipeReadResult = await pipeReader.ReadAsync();
                        var buffer = pipeReadResult.Buffer;
    
                        try
                        {
                            //process data in buffer
                            Console.WriteLine(buffer.Length.ToString());
    
                            if (pipeReadResult.IsCompleted)
                            {
                                break;
                            }
                        }
                        finally
                        {
                            pipeReader.AdvanceTo(buffer.End);
                        }
                    }
                }
    

    如果将 PipeReader 的缓冲区大小设置为大于要读取的文件大小,则不需要循环,但这超出了使用 PipeReader 的目的 - 逐个处理动态数据。

    我认为 PipeReader 不适合您的情况。 Utf8JsonReader 可以直接处理文件流。我想这就是你需要的 -

    https://docs.microsoft.com/en-us/dotnet/standard/serialization/write-custom-serializer-deserializer#read-from-a-stream-using-utf8jsonreader

    【讨论】:

    • 我的问题的目标是将 Utf8JsonReader 读取,就像 webapi 管道一样传递给我的自定义 JsonConverter...
    猜你喜欢
    • 2020-06-21
    • 1970-01-01
    • 2014-04-22
    • 1970-01-01
    • 2021-12-21
    • 2017-12-23
    • 1970-01-01
    • 2022-12-12
    • 2019-11-22
    相关资源
    最近更新 更多