簡體   English   中英

如何使用Json.net作為流將JSON轉換為BSON

[英]How to convert JSON to BSON using Json.net AS A STREAM

我想將JSON文件轉換為BSON文件。 使用JsonTextReaderBsonDataWriter的方法JsonTextReader BsonDataWriter

這是代碼:

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文件。 使用流是否可以?

BsonDataWriter繼承自JsonWriter因此您可以使用JsonWriter.WriteToken(JsonReader)從JSON流復制到BSON流(反之亦然,使用BsonDataReader ):

public static class JsonExtensions
{
    public static void CopyToBson(string inputPath, string outputPath, FileMode fileMode = FileMode.CreateNew)
    {
        using ( var textReader = File.OpenText(inputPath) )
        using ( var jsonReader = new JsonTextReader( textReader ))
        using ( var oFileStream = new FileStream( outputPath, fileMode ) )
        using ( var dataWriter = new BsonDataWriter(oFileStream) )
        {
            dataWriter.WriteToken(jsonReader);
        }
    }
}

筆記:

  1. 您可能需要添加錯誤處理,以在發生錯誤時刪除部分創建的輸出文件。

  2. BSON文檔的根令牌必須是一個對象或數組,因此僅由原始值組成的JSON輸入將導致此方法引發錯誤。

  3. 根據BSON規范 ,數組是一個普通的BSON文檔,其密鑰的整數值從0開始,並依次連續。 因此,如果將包含數組的JSON轉換為BSON,然后將BSON加載到JToken (或dynamic )中,則將獲得帶有數字鍵而不是數組的對象。

  4. BSON支持已移至Json.NET 10.0.1其自己的軟件包Newtonsoft.Json.Bson中。 在早期版本中,請使用BsonWriter

  5. 即使您正在使用流,如對Json.Net中Streams和BsonWriter的OutOfMemory Exception的 回答中所解釋的,您可能也無法獲得所需的內存性能:

    根據BSON規范 ,每個對象或數組-在標准中稱為文檔 -必須在開始時包含構成文檔的字節總數的計數...

    Newtonsoft的BsonDataWriter和基礎BsonBinaryWriter通過將所有要寫入樹中的令牌緩存起來,然后在確定完根令牌的內容之后,在寫出樹之前遞歸計算大小來實現此目的。

演示小提琴#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;
    }
}

然后按以下方式使用它:

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);

筆記:

  1. 我專門針對數組的情況實現了流式+尋找,因為這似乎是處理大型JSON文件的最常見情況。

  2. 話雖如此,它可以通過遵循標准中document規范進行擴展以流式傳輸JSON對象,並且可以通過增強BsonExtensions.GetBsonType()並對其進行適當格式化來擴展以處理原始值。

  3. 這樣,例程就可以遞歸地調用自身,當根對象包含一個非常大的數組作為成員時,這可能很有用。 (不過,到目前為止,您基本上已經編寫了自己的BsonDataWriter版本。)

    但是,這樣做可能會在輸出流中導致大量搜索,這可能會極大地影響性能。

演示小提琴#2 在這里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM