Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用JSON.net作为流将JSON转换为BSON_C#_Json_Json.net_Bson - Fatal编程技术网

C# 如何使用JSON.net作为流将JSON转换为BSON

C# 如何使用JSON.net作为流将JSON转换为BSON,c#,json,json.net,bson,C#,Json,Json.net,Bson,我想做一个JSON文件到BSON文件的流式转换。考虑到JsonTextReader和的方法,这可能吗 代码如下: using ( StreamReader textReader = File.OpenText(@"k:\\BrokeredMessage_Alarmhub-Infra-Prd-Sbn_08-06-2019 11-13-34.json" ) ) using ( JsonTextReader jsonTextReader = new JsonTextReader( textReader

我想做一个JSON文件到BSON文件的流式转换。考虑到
JsonTextReader
和的方法,这可能吗

代码如下:

using ( StreamReader textReader = File.OpenText(@"k:\\BrokeredMessage_Alarmhub-Infra-Prd-Sbn_08-06-2019 11-13-34.json" ) )
using ( JsonTextReader jsonTextReader = new JsonTextReader( textReader ))
using ( FileStream oFileStream = new FileStream( @"k:\\output.bson", FileMode.CreateNew ) )
using ( BsonDataWriter datawriter = new BsonDataWriter (oFileStream) )
{
   ...
}
我不想反序列化JSON文件的全部内容,因为我想读取JSON文件并以最小的内存负载写入BSON文件。这可以通过使用流实现吗?

继承自,因此您可以使用从JSON流复制到BSON流(反之亦然,使用):

注:

  • 您可能希望添加错误处理,以便在发生错误时删除部分创建的输出文件

  • BSON文档的根标记必须是对象或数组,因此仅包含原语值的JSON输入将导致此方法抛出错误

  • 根据定义,数组是一个普通的BSON文档,其键为整数值,从0开始,按顺序继续。因此,如果您将包含数组的JSON转换为BSON,然后将BSON加载到
    JToken
    (或
    dynamic
    )中,您将得到一个带有数字键的对象,而不是数组

  • BSON支持被转移到Json.NET中自己的包中。在早期版本中使用

  • 即使您使用的是流,如中所述,您也可能无法获得希望的内存性能:

    根据,每个对象或数组(标准中称为文档)必须在开始时包含组成文档的总字节数计数

    Newtonsoft及其底层通过缓存要写入树中的所有内容来实现这一点,然后在根令牌的内容完成后,在写入树之前递归计算大小

  • 演示小提琴#1


    如果
    BsonDataWriter
    创建的令牌缓存超出了系统内存,则需要手动实现一种算法,将
    JsonReader
    流式处理到BSON流,在完成后返回输出流以写出最终对象大小

    例如,假设您的根JSON容器是一个JSON对象数组。然后,以下方法将增量序列化数组,然后在流中查找以写入总大小:

    public static partial class BsonExtensions
    {
        public static void CopyJsonToBson(string inputPath, string outputPath, FileMode fileMode)
        {
            using ( var textReader = File.OpenText(inputPath) )
            using ( var jsonReader = new JsonTextReader( textReader ))
            using ( var oFileStream = new FileStream( outputPath, fileMode ) )
            {
                CopyJsonToBson(jsonReader, oFileStream);
            }
        }
    
        public static void CopyJsonToBson(JsonReader jsonReader, Stream stream)
        {
            var rootTokenType = jsonReader.ReadToContentAndAssert().TokenType;
            if (!stream.CanSeek || rootTokenType != JsonToken.StartArray)
            {
                using ( var dataWriter = new BsonDataWriter(stream) { CloseOutput = false } )
                {
                    dataWriter.WriteToken(jsonReader, stream.CanSeek);
                }
            }
            else
            {
                stream.Flush(); // Just in case.
    
                var initialPosition = stream.Position;
                var buffer = new byte[256];
    
                WriteInt(stream, 0, buffer); // CALCULATED SIZE TO BE CALCULATED LATER.
    
                ulong index = 0;
    
                while (jsonReader.ReadToContentAndAssert().TokenType != JsonToken.EndArray)
                {
                    var bsonType = GetBsonType(jsonReader.TokenType, jsonReader.ValueType);
                    stream.WriteByte(unchecked((byte)bsonType));
                    WriteString(stream, index.ToString(NumberFormatInfo.InvariantInfo), buffer);
                    using (var dataWriter = new BsonDataWriter(stream) { CloseOutput = false })
                    {
                        dataWriter.WriteToken(jsonReader);
                    }
                    index++;
                }
    
                stream.WriteByte((byte)0);
                stream.Flush();
    
                var finalPosition = stream.Position;
                stream.Position = initialPosition;
    
                var size = checked((int)(finalPosition - initialPosition));
                WriteInt(stream, size, buffer); // CALCULATED SIZE TO BE CALCULATED LATER.
    
                stream.Position = finalPosition;
            }
        }
    
        private static readonly Encoding Encoding = new UTF8Encoding(false);
    
        private static void WriteString(Stream stream, string s, byte[] buffer)
        {
            if (s != null)
            {
                if (s.Length < buffer.Length / Encoding.GetMaxByteCount(1))
                {
                    var byteCount = Encoding.GetBytes(s, 0, s.Length, buffer, 0);
                    stream.Write(buffer, 0, byteCount);
                }
                else
                {
                    byte[] bytes = Encoding.GetBytes(s);
                    stream.Write(bytes, 0, bytes.Length);
                }
            }
    
            stream.WriteByte((byte)0);
        }       
    
        private static void WriteInt(Stream stream, int value, byte[] buffer)
        {
            unchecked
            {
                buffer[0] = (byte) value;
                buffer[1] = (byte) (value >> 8);
                buffer[2] = (byte) (value >> 16);
                buffer[3] = (byte) (value >> 24);
            }
            stream.Write(buffer, 0, 4);
        }
    
        private static BsonType GetBsonType(JsonToken jsonType, Type valueType)
        {
            switch (jsonType)
            {
                case JsonToken.StartArray:
                    return BsonType.Array;
    
                case JsonToken.StartObject:
                    return BsonType.Object;
    
                case JsonToken.Null:
                    return BsonType.Null;
    
                // Add primitives as required.
    
                default:
                    throw new JsonWriterException(string.Format("BsonType for {0} not implemented.", jsonType));
            }
        }
    
        //Copied from: https://github.com/JamesNK/Newtonsoft.Json.Bson/blob/master/Src/Newtonsoft.Json.Bson/BsonType.cs
        //Original source: http://bsonspec.org/spec.html
        enum BsonType : sbyte
        {
            Number = 1,
            String = 2,
            Object = 3,
            Array = 4,
            Binary = 5,
            Undefined = 6,
            Oid = 7,
            Boolean = 8,
            Date = 9,
            Null = 10,
            Regex = 11,
            Reference = 12,
            Code = 13,
            Symbol = 14,
            CodeWScope = 15,
            Integer = 16,
            TimeStamp = 17,
            Long = 18,
            MinKey = -1,
            MaxKey = 127
        }       
    }
    
    public static partial class JsonExtensions
    {
        public static JsonReader ReadToContentAndAssert(this JsonReader reader)
        {
            return reader.ReadAndAssert().MoveToContentAndAssert();
        }
    
        public static JsonReader MoveToContentAndAssert(this JsonReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException();
            if (reader.TokenType == JsonToken.None)       // Skip past beginning of stream.
                reader.ReadAndAssert();
            while (reader.TokenType == JsonToken.Comment) // Skip past comments.
                reader.ReadAndAssert();
            return reader;
        }
    
        public static JsonReader ReadAndAssert(this JsonReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException();
            if (!reader.Read())
                throw new JsonReaderException("Unexpected end of JSON stream.");
            return reader;
        }
    }
    
    注:

  • 我专门针对数组实现了streaming+seeking,因为这似乎是大型JSON文件最常见的场景

  • 这就是说,它可以通过遵循中的
    文档
    规范来扩展到流式JSON对象,也可以通过增强
    BsonExtensions.GetBsonType()
    并对其进行适当的格式化来扩展到处理基元值

  • 这样做之后,例程就可以递归地调用自己,当根对象包含一个非常大的数组作为成员时,这可能很有用。(不过,此时,您基本上已经编写了自己版本的
    BsonDataWriter

    但是,这样做可能会在输出流中导致大量的寻道,这可能会极大地影响性能

  • Demo fiddle#2.

    继承自,因此您可以使用从JSON流复制到BSON流(反之亦然,使用):

    注:

  • 您可能希望添加错误处理,以便在发生错误时删除部分创建的输出文件

  • BSON文档的根标记必须是对象或数组,因此仅包含原语值的JSON输入将导致此方法抛出错误

  • 根据定义,数组是一个普通的BSON文档,其键为整数值,从0开始,按顺序继续。因此,如果您将包含数组的JSON转换为BSON,然后将BSON加载到
    JToken
    (或
    dynamic
    )中,您将得到一个带有数字键的对象,而不是数组

  • BSON支持被转移到Json.NET中自己的包中。在早期版本中使用

  • 即使您使用的是流,如中所述,您也可能无法获得希望的内存性能:

    根据,每个对象或数组(标准中称为文档)必须在开始时包含组成文档的总字节数计数

    Newtonsoft及其底层通过缓存要写入树中的所有内容来实现这一点,然后在根令牌的内容完成后,在写入树之前递归计算大小

  • 演示小提琴#1


    如果
    BsonDataWriter
    创建的令牌缓存超出了系统内存,则需要手动实现一种算法,将
    JsonReader
    流式处理到BSON流,在完成后返回输出流以写出最终对象大小

    例如,假设您的根JSON容器是一个JSON对象数组。然后,以下方法将增量序列化数组,然后在流中查找以写入总大小:

    public static partial class BsonExtensions
    {
        public static void CopyJsonToBson(string inputPath, string outputPath, FileMode fileMode)
        {
            using ( var textReader = File.OpenText(inputPath) )
            using ( var jsonReader = new JsonTextReader( textReader ))
            using ( var oFileStream = new FileStream( outputPath, fileMode ) )
            {
                CopyJsonToBson(jsonReader, oFileStream);
            }
        }
    
        public static void CopyJsonToBson(JsonReader jsonReader, Stream stream)
        {
            var rootTokenType = jsonReader.ReadToContentAndAssert().TokenType;
            if (!stream.CanSeek || rootTokenType != JsonToken.StartArray)
            {
                using ( var dataWriter = new BsonDataWriter(stream) { CloseOutput = false } )
                {
                    dataWriter.WriteToken(jsonReader, stream.CanSeek);
                }
            }
            else
            {
                stream.Flush(); // Just in case.
    
                var initialPosition = stream.Position;
                var buffer = new byte[256];
    
                WriteInt(stream, 0, buffer); // CALCULATED SIZE TO BE CALCULATED LATER.
    
                ulong index = 0;
    
                while (jsonReader.ReadToContentAndAssert().TokenType != JsonToken.EndArray)
                {
                    var bsonType = GetBsonType(jsonReader.TokenType, jsonReader.ValueType);
                    stream.WriteByte(unchecked((byte)bsonType));
                    WriteString(stream, index.ToString(NumberFormatInfo.InvariantInfo), buffer);
                    using (var dataWriter = new BsonDataWriter(stream) { CloseOutput = false })
                    {
                        dataWriter.WriteToken(jsonReader);
                    }
                    index++;
                }
    
                stream.WriteByte((byte)0);
                stream.Flush();
    
                var finalPosition = stream.Position;
                stream.Position = initialPosition;
    
                var size = checked((int)(finalPosition - initialPosition));
                WriteInt(stream, size, buffer); // CALCULATED SIZE TO BE CALCULATED LATER.
    
                stream.Position = finalPosition;
            }
        }
    
        private static readonly Encoding Encoding = new UTF8Encoding(false);
    
        private static void WriteString(Stream stream, string s, byte[] buffer)
        {
            if (s != null)
            {
                if (s.Length < buffer.Length / Encoding.GetMaxByteCount(1))
                {
                    var byteCount = Encoding.GetBytes(s, 0, s.Length, buffer, 0);
                    stream.Write(buffer, 0, byteCount);
                }
                else
                {
                    byte[] bytes = Encoding.GetBytes(s);
                    stream.Write(bytes, 0, bytes.Length);
                }
            }
    
            stream.WriteByte((byte)0);
        }       
    
        private static void WriteInt(Stream stream, int value, byte[] buffer)
        {
            unchecked
            {
                buffer[0] = (byte) value;
                buffer[1] = (byte) (value >> 8);
                buffer[2] = (byte) (value >> 16);
                buffer[3] = (byte) (value >> 24);
            }
            stream.Write(buffer, 0, 4);
        }
    
        private static BsonType GetBsonType(JsonToken jsonType, Type valueType)
        {
            switch (jsonType)
            {
                case JsonToken.StartArray:
                    return BsonType.Array;
    
                case JsonToken.StartObject:
                    return BsonType.Object;
    
                case JsonToken.Null:
                    return BsonType.Null;
    
                // Add primitives as required.
    
                default:
                    throw new JsonWriterException(string.Format("BsonType for {0} not implemented.", jsonType));
            }
        }
    
        //Copied from: https://github.com/JamesNK/Newtonsoft.Json.Bson/blob/master/Src/Newtonsoft.Json.Bson/BsonType.cs
        //Original source: http://bsonspec.org/spec.html
        enum BsonType : sbyte
        {
            Number = 1,
            String = 2,
            Object = 3,
            Array = 4,
            Binary = 5,
            Undefined = 6,
            Oid = 7,
            Boolean = 8,
            Date = 9,
            Null = 10,
            Regex = 11,
            Reference = 12,
            Code = 13,
            Symbol = 14,
            CodeWScope = 15,
            Integer = 16,
            TimeStamp = 17,
            Long = 18,
            MinKey = -1,
            MaxKey = 127
        }       
    }
    
    public static partial class JsonExtensions
    {
        public static JsonReader ReadToContentAndAssert(this JsonReader reader)
        {
            return reader.ReadAndAssert().MoveToContentAndAssert();
        }
    
        public static JsonReader MoveToContentAndAssert(this JsonReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException();
            if (reader.TokenType == JsonToken.None)       // Skip past beginning of stream.
                reader.ReadAndAssert();
            while (reader.TokenType == JsonToken.Comment) // Skip past comments.
                reader.ReadAndAssert();
            return reader;
        }
    
        public static JsonReader ReadAndAssert(this JsonReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException();
            if (!reader.Read())
                throw new JsonReaderException("Unexpected end of JSON stream.");
            return reader;
        }
    }
    
    注:

  • 我专门针对数组实现了streaming+seeking,因为这似乎是大型JSON文件最常见的场景

  • 这就是说,它可以通过遵循中的
    文档
    规范来扩展到流式JSON对象,也可以通过增强
    BsonExtensions.GetBsonType()
    并对其进行适当的格式化来扩展到处理基元值

  • 这样做之后,例程就可以递归地调用自己,当根对象包含一个非常大的数组作为成员时,这可能很有用。(不过,此时,您基本上已经编写了自己版本的
    BsonDataWriter

    然而,这样做可能会在输出流中导致大量的寻道,这可能会极大地影响每一次寻道
    var inputPath = @"k:\\BrokeredMessage_Alarmhub-Infra-Prd-Sbn_08-06-2019 11-13-34.json";
    var outputPath = @"k:\\output.bson";
    
    BsonExtensions.CopyJsonToBson(inputPath, outputPath, FileMode.Create);