diff --git a/Src/Newtonsoft.Json.Tests/Bson/BsonReaderTests.cs b/Src/Newtonsoft.Json.Tests/Bson/BsonReaderTests.cs index 95880fdd..ffb53252 100644 --- a/Src/Newtonsoft.Json.Tests/Bson/BsonReaderTests.cs +++ b/Src/Newtonsoft.Json.Tests/Bson/BsonReaderTests.cs @@ -31,6 +31,7 @@ using NUnit.Framework; using Newtonsoft.Json.Bson; using System.IO; using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; namespace Newtonsoft.Json.Tests.Bson { @@ -176,5 +177,364 @@ namespace Newtonsoft.Json.Tests.Bson string decodedString = Encoding.UTF8.GetString(encodedStringData); Assert.AreEqual("Hello world!", decodedString); } + + [Test] + public void ReadOid() + { + byte[] data = MiscellaneousUtils.HexToBytes("29000000075F6964004ABBED9D1D8B0F02180000010274657374000900000031323334C2A335360000"); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("_id", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Bytes, reader.TokenType); + Assert.AreEqual(MiscellaneousUtils.HexToBytes("4ABBED9D1D8B0F0218000001"), reader.Value); + Assert.AreEqual(typeof(byte[]), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("test", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("1234£56", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadNestedArray() + { + string hexdoc = "82-00-00-00-07-5F-69-64-00-4A-78-93-79-17-22-00-00-00-00-61-CF-04-61-00-5D-00-00-00-01-30-00-00-00-00-00-00-00-F0-3F-01-31-00-00-00-00-00-00-00-00-40-01-32-00-00-00-00-00-00-00-08-40-01-33-00-00-00-00-00-00-00-10-40-01-34-00-00-00-00-00-00-00-14-50-01-35-00-00-00-00-00-00-00-18-40-01-36-00-00-00-00-00-00-00-1C-40-01-37-00-00-00-00-00-00-00-20-40-00-02-62-00-05-00-00-00-74-65-73-74-00-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("_id", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Bytes, reader.TokenType); + Assert.AreEqual(MiscellaneousUtils.HexToBytes("4A-78-93-79-17-22-00-00-00-00-61-CF"), reader.Value); + Assert.AreEqual(typeof(byte[]), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("a", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + for (int i = 1; i <= 8; i++) + { + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + + double value = (i != 5) + ? Convert.ToDouble(i) + : 5.78960446186581E+77d; + + Assert.AreEqual(value, reader.Value); + } + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("b", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("test", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadNestedArrayIntoLinq() + { + string hexdoc = "87-00-00-00-05-5F-69-64-00-0C-00-00-00-02-4A-78-93-79-17-22-00-00-00-00-61-CF-04-61-00-5D-00-00-00-01-30-00-00-00-00-00-00-00-F0-3F-01-31-00-00-00-00-00-00-00-00-40-01-32-00-00-00-00-00-00-00-08-40-01-33-00-00-00-00-00-00-00-10-40-01-34-00-00-00-00-00-00-00-14-50-01-35-00-00-00-00-00-00-00-18-40-01-36-00-00-00-00-00-00-00-1C-40-01-37-00-00-00-00-00-00-00-20-40-00-02-62-00-05-00-00-00-74-65-73-74-00-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + BsonReader reader = new BsonReader(new MemoryStream(data)); + + JObject o = (JObject)JToken.ReadFrom(reader); + Assert.AreEqual(3, o.Count); + + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + o.WriteTo(writer); + writer.Flush(); + + string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); + Assert.AreEqual(hexdoc, bson); + } + + [Test] + public void OidAndBytesAreEqual() + { + byte[] data1 = MiscellaneousUtils.HexToBytes( + "82-00-00-00-07-5F-69-64-00-4A-78-93-79-17-22-00-00-00-00-61-CF-04-61-00-5D-00-00-00-01-30-00-00-00-00-00-00-00-F0-3F-01-31-00-00-00-00-00-00-00-00-40-01-32-00-00-00-00-00-00-00-08-40-01-33-00-00-00-00-00-00-00-10-40-01-34-00-00-00-00-00-00-00-14-50-01-35-00-00-00-00-00-00-00-18-40-01-36-00-00-00-00-00-00-00-1C-40-01-37-00-00-00-00-00-00-00-20-40-00-02-62-00-05-00-00-00-74-65-73-74-00-00"); + + BsonReader reader1 = new BsonReader(new MemoryStream(data1)); + + // oid + JObject o1 = (JObject)JToken.ReadFrom(reader1); + + byte[] data2 = MiscellaneousUtils.HexToBytes( + "87-00-00-00-05-5F-69-64-00-0C-00-00-00-02-4A-78-93-79-17-22-00-00-00-00-61-CF-04-61-00-5D-00-00-00-01-30-00-00-00-00-00-00-00-F0-3F-01-31-00-00-00-00-00-00-00-00-40-01-32-00-00-00-00-00-00-00-08-40-01-33-00-00-00-00-00-00-00-10-40-01-34-00-00-00-00-00-00-00-14-50-01-35-00-00-00-00-00-00-00-18-40-01-36-00-00-00-00-00-00-00-1C-40-01-37-00-00-00-00-00-00-00-20-40-00-02-62-00-05-00-00-00-74-65-73-74-00-00"); + + BsonReader reader2 = new BsonReader(new MemoryStream(data2)); + + // bytes + JObject o2 = (JObject)JToken.ReadFrom(reader2); + + Assert.IsTrue(o1.DeepEquals(o2)); + } + + [Test] + public void ReadRegex() + { + string hexdoc = "15-00-00-00-0B-72-65-67-65-78-00-74-65-73-74-00-67-69-6D-00-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("regex", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(@"/test/gim", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadCode() + { + string hexdoc = "1A-00-00-00-0D-63-6F-64-65-00-0B-00-00-00-49-20-61-6D-20-63-6F-64-65-21-00-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("code", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(@"I am code!", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadUndefined() + { + string hexdoc = "10-00-00-00-06-75-6E-64-65-66-69-6E-65-64-00-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("undefined", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Undefined, reader.TokenType); + Assert.AreEqual(null, reader.Value); + Assert.AreEqual(null, reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadLong() + { + string hexdoc = "13-00-00-00-12-6C-6F-6E-67-00-FF-FF-FF-FF-FF-FF-FF-7F-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("long", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual(long.MaxValue, reader.Value); + Assert.AreEqual(typeof(long), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadReference() + { + string hexdoc = "1E-00-00-00-0C-6F-69-64-00-04-00-00-00-6F-69-64-00-01-02-03-04-05-06-07-08-09-0A-0B-0C-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("oid", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("$ref", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("oid", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("$id", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Bytes, reader.TokenType); + Assert.AreEqual(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }, reader.Value); + Assert.AreEqual(typeof(byte[]), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadCodeWScope() + { + string hexdoc = "75-00-00-00-0F-63-6F-64-65-57-69-74-68-53-63-6F-70-65-00-61-00-00-00-35-00-00-00-66-6F-72-20-28-69-6E-74-20-69-20-3D-20-30-3B-20-69-20-3C-20-31-30-30-30-3B-20-69-2B-2B-29-0D-0A-7B-0D-0A-20-20-61-6C-65-72-74-28-61-72-67-31-29-3B-0D-0A-7D-00-24-00-00-00-02-61-72-67-31-00-15-00-00-00-4A-73-6F-6E-2E-4E-45-54-20-69-73-20-61-77-65-73-6F-6D-65-2E-00-00-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("codeWithScope", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("$code", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(@"for (int i = 0; i < 1000; i++) +{ + alert(arg1); +}", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("$scope", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("arg1", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("Json.NET is awesome.", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadEndOfStream() + { + BsonReader reader = new BsonReader(new MemoryStream()); + Assert.IsFalse(reader.Read()); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Bson/BsonWriterTests.cs b/Src/Newtonsoft.Json.Tests/Bson/BsonWriterTests.cs index f3ce880b..9699a2e1 100644 --- a/Src/Newtonsoft.Json.Tests/Bson/BsonWriterTests.cs +++ b/Src/Newtonsoft.Json.Tests/Bson/BsonWriterTests.cs @@ -31,6 +31,7 @@ using NUnit.Framework; using Newtonsoft.Json.Bson; using System.IO; using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Tests.TestObjects; namespace Newtonsoft.Json.Tests.Bson { @@ -46,25 +47,11 @@ namespace Newtonsoft.Json.Tests.Bson writer.WritePropertyName("Blah"); writer.WriteValue(1); writer.WriteEndObject(); - writer.Flush(); string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); Assert.AreEqual("0F-00-00-00-10-42-6C-61-68-00-01-00-00-00-00", bson); } - [Test] - [ExpectedException(typeof(JsonWriterException), ExpectedMessage = "Cannot flush BsonWriter until JSON is complete.")] - public void FlushInsideObject() - { - MemoryStream ms = new MemoryStream(); - BsonWriter writer = new BsonWriter(ms); - - writer.WriteStartObject(); - writer.WritePropertyName("Blah"); - writer.WriteValue(1); - writer.Flush(); - } - [Test] public void WriteArrayBsonFromSite() { @@ -108,5 +95,100 @@ namespace Newtonsoft.Json.Tests.Bson Assert.AreEqual(expected, bson); } + + [Test] + public void WriteNestedArray() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + writer.WriteStartObject(); + + writer.WritePropertyName("_id"); + writer.WriteValue(MiscellaneousUtils.HexToBytes("4A-78-93-79-17-22-00-00-00-00-61-CF")); + + writer.WritePropertyName("a"); + writer.WriteStartArray(); + for (int i = 1; i <= 8; i++) + { + double value = (i != 5) + ? Convert.ToDouble(i) + : 5.78960446186581E+77d; + + writer.WriteValue(value); + } + writer.WriteEndArray(); + + writer.WritePropertyName("b"); + writer.WriteValue("test"); + + writer.WriteEndObject(); + + writer.Flush(); + + ms.Seek(0, SeekOrigin.Begin); + + string expected = "87-00-00-00-05-5F-69-64-00-0C-00-00-00-02-4A-78-93-79-17-22-00-00-00-00-61-CF-04-61-00-5D-00-00-00-01-30-00-00-00-00-00-00-00-F0-3F-01-31-00-00-00-00-00-00-00-00-40-01-32-00-00-00-00-00-00-00-08-40-01-33-00-00-00-00-00-00-00-10-40-01-34-00-00-00-00-00-00-00-14-50-01-35-00-00-00-00-00-00-00-18-40-01-36-00-00-00-00-00-00-00-1C-40-01-37-00-00-00-00-00-00-00-20-40-00-02-62-00-05-00-00-00-74-65-73-74-00-00"; + string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); + + Assert.AreEqual(expected, bson); + } + + [Test] + public void WriteSerializedStore() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + Store s1 = new Store(); + s1.Color = StoreColor.White; + s1.Cost = 999.59m; + s1.Employees = int.MaxValue - 1; + s1.Open = true; + s1.product.Add(new Product + { + ExpiryDate = new DateTime(2000, 9, 28, 3, 59, 58, DateTimeKind.Utc), + Name = "BSON!", + Price = -0.1m, + Sizes = new [] { "First", "Second" } + }); + + JsonSerializer serializer = new JsonSerializer(); + serializer.Serialize(writer, s1); + + ms.Seek(0, SeekOrigin.Begin); + BsonReader reader = new BsonReader(ms); + Store s2 = (Store)serializer.Deserialize(reader, typeof (Store)); + + Assert.AreNotEqual(s1, s2); + Assert.AreEqual(s1.Color, s2.Color); + Assert.AreEqual(s1.Cost, s2.Cost); + Assert.AreEqual(s1.Employees, s2.Employees); + Assert.AreEqual(s1.Escape, s2.Escape); + Assert.AreEqual(s1.Establised, s2.Establised); + Assert.AreEqual(s1.Mottos.Count, s2.Mottos.Count); + Assert.AreEqual(s1.Mottos.First(), s2.Mottos.First()); + Assert.AreEqual(s1.Mottos.Last(), s2.Mottos.Last()); + Assert.AreEqual(s1.Open, s2.Open); + Assert.AreEqual(s1.product.Count, s2.product.Count); + Assert.AreEqual(s1.RoomsPerFloor.Length, s2.RoomsPerFloor.Length); + Assert.AreEqual(s1.Symbol, s2.Symbol); + Assert.AreEqual(s1.Width, s2.Width); + + MemoryStream ms1 = new MemoryStream(); + BsonWriter writer1 = new BsonWriter(ms1); + + serializer.Serialize(writer1, s1); + + Assert.AreEqual(ms.ToArray(), ms1.ToArray()); + + string s = JsonConvert.SerializeObject(s1); + byte[] textData = Encoding.UTF8.GetBytes(s); + + int l1 = textData.Length; + int l2 = ms.ToArray().Length; + + Console.WriteLine(l1); + Console.WriteLine(l2); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Bson/BsonReader.cs b/Src/Newtonsoft.Json/Bson/BsonReader.cs index f0e421de..2c068eb2 100644 --- a/Src/Newtonsoft.Json/Bson/BsonReader.cs +++ b/Src/Newtonsoft.Json/Bson/BsonReader.cs @@ -34,6 +34,19 @@ using Newtonsoft.Json.Linq; namespace Newtonsoft.Json.Bson { + internal enum BsonReaderState + { + Normal, + ReferenceStart, + ReferenceRef, + ReferenceId, + CodeWScopeStart, + CodeWScopeCode, + CodeWScopeScope, + CodeWScopeScopeObject, + CodeWScopeScopeEnd + } + public class BsonReader : JsonReader { private readonly BinaryReader _reader; @@ -41,13 +54,24 @@ namespace Newtonsoft.Json.Bson private readonly List _stack; private BsonType _currentElementType; - + private BsonReaderState _bsonReaderState; private class ContainerContext { public JTokenType Type { get; private set; } public int Length { get; set; } - public int Position { get; set; } + private int _position; + public int Position + { + get { return _position; } + set + { + if (value > Length && Length != 0) + throw new JsonReaderException("Read past end of current container context."); + + _position = value; + } + } public ContainerContext(JTokenType type) { @@ -87,89 +111,23 @@ namespace Newtonsoft.Json.Bson { try { - switch (CurrentState) + switch (_bsonReaderState) { - case State.Start: - { - JsonToken token = (!_rootTypeIsArray) ? JsonToken.StartObject : JsonToken.StartArray; - JTokenType type = (!_rootTypeIsArray) ? JTokenType.Object : JTokenType.Array; - - SetToken(token); - ContainerContext newContext = new ContainerContext(type); - _stack.Add(newContext); - newContext.Length = ReadInt32(); - return true; - } - case State.ObjectStart: - { - SetToken(JsonToken.PropertyName, ReadElement()); - return true; - } - case State.Complete: - break; - case State.Property: - { - ReadType(_currentElementType); - return true; - } - case State.Object: - break; - case State.ArrayStart: - ReadElement(); - ReadType(_currentElementType); - return true; - case State.Array: - break; - case State.Closed: - break; - case State.PostValue: - ContainerContext context = GetCurrentContext(); - if (context == null) - return false; - - int lengthMinusEnd = context.Length - 1; - - if (context.Position < lengthMinusEnd) - { - if (context.Type == JTokenType.Array) - { - ReadElement(); - ReadType(_currentElementType); - return true; - } - else - { - SetToken(JsonToken.PropertyName, ReadElement()); - return true; - } - } - else if (context.Position == lengthMinusEnd) - { - if (ReadByte() != 0) - throw new JsonReaderException("Unexpected end of object byte value."); - - _stack.RemoveAt(_stack.Count - 1); - if (_stack.Count != 0) - MovePosition(context.Length); - - JsonToken endToken = (context.Type == JTokenType.Object) ? JsonToken.EndObject : JsonToken.EndArray; - SetToken(endToken); - return true; - } - break; - case State.ConstructorStart: - break; - case State.Constructor: - break; - case State.Error: - break; - case State.Finished: - break; + case BsonReaderState.Normal: + return ReadNormal(); + case BsonReaderState.ReferenceStart: + case BsonReaderState.ReferenceRef: + case BsonReaderState.ReferenceId: + return ReadReference(); + case BsonReaderState.CodeWScopeStart: + case BsonReaderState.CodeWScopeCode: + case BsonReaderState.CodeWScopeScope: + case BsonReaderState.CodeWScopeScopeObject: + case BsonReaderState.CodeWScopeScopeEnd: + return ReadCodeWScope(); default: - throw new ArgumentOutOfRangeException(); + throw new JsonReaderException("Unexpected state: {0}".FormatWith(CultureInfo.InvariantCulture, _bsonReaderState)); } - - return false; } catch (EndOfStreamException) { @@ -177,6 +135,189 @@ namespace Newtonsoft.Json.Bson } } + private bool ReadCodeWScope() + { + switch (_bsonReaderState) + { + case BsonReaderState.CodeWScopeStart: + SetToken(JsonToken.PropertyName, "$code"); + _bsonReaderState = BsonReaderState.CodeWScopeCode; + return true; + case BsonReaderState.CodeWScopeCode: + // total CodeWScope size - not used + ReadInt32(); + + SetToken(JsonToken.String, ReadLengthString()); + _bsonReaderState = BsonReaderState.CodeWScopeScope; + return true; + case BsonReaderState.CodeWScopeScope: + if (CurrentState == State.PostValue) + { + SetToken(JsonToken.PropertyName, "$scope"); + return true; + } + else + { + SetToken(JsonToken.StartObject); + _bsonReaderState = BsonReaderState.CodeWScopeScopeObject; + + ContainerContext newContext = new ContainerContext(JTokenType.Object); + _stack.Add(newContext); + newContext.Length = ReadInt32(); + + return true; + } + case BsonReaderState.CodeWScopeScopeObject: + bool result = ReadNormal(); + if (result && TokenType == JsonToken.EndObject) + _bsonReaderState = BsonReaderState.CodeWScopeScopeEnd; + + return result; + case BsonReaderState.CodeWScopeScopeEnd: + SetToken(JsonToken.EndObject); + _bsonReaderState = BsonReaderState.Normal; + return true; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private bool ReadReference() + { + switch (CurrentState) + { + case State.ObjectStart: + { + SetToken(JsonToken.PropertyName, "$ref"); + _bsonReaderState = BsonReaderState.ReferenceRef; + return true; + } + case State.Property: + { + if (_bsonReaderState == BsonReaderState.ReferenceRef) + { + SetToken(JsonToken.String, ReadLengthString()); + return true; + } + else if (_bsonReaderState == BsonReaderState.ReferenceId) + { + SetToken(JsonToken.Bytes, ReadBytes(12)); + return true; + } + else + { + throw new JsonReaderException("Unexpected state when reading BSON reference: " + _bsonReaderState); + } + } + case State.PostValue: + { + if (_bsonReaderState == BsonReaderState.ReferenceRef) + { + SetToken(JsonToken.PropertyName, "$id"); + _bsonReaderState = BsonReaderState.ReferenceId; + return true; + } + else if (_bsonReaderState == BsonReaderState.ReferenceId) + { + SetToken(JsonToken.EndObject); + _bsonReaderState = BsonReaderState.Normal; + return true; + } + else + { + throw new JsonReaderException("Unexpected state when reading BSON reference: " + _bsonReaderState); + } + } + default: + throw new JsonReaderException("Unexpected state when reading BSON reference: " + CurrentState); + } + } + + private bool ReadNormal() + { + switch (CurrentState) + { + case State.Start: + { + JsonToken token = (!_rootTypeIsArray) ? JsonToken.StartObject : JsonToken.StartArray; + JTokenType type = (!_rootTypeIsArray) ? JTokenType.Object : JTokenType.Array; + + SetToken(token); + ContainerContext newContext = new ContainerContext(type); + _stack.Add(newContext); + newContext.Length = ReadInt32(); + return true; + } + case State.ObjectStart: + { + SetToken(JsonToken.PropertyName, ReadElement()); + return true; + } + case State.Complete: + case State.Closed: + return false; + case State.Property: + { + ReadType(_currentElementType); + return true; + } + case State.ArrayStart: + ReadElement(); + ReadType(_currentElementType); + return true; + case State.PostValue: + ContainerContext context = GetCurrentContext(); + if (context == null) + return false; + + int lengthMinusEnd = context.Length - 1; + + if (context.Position < lengthMinusEnd) + { + if (context.Type == JTokenType.Array) + { + ReadElement(); + ReadType(_currentElementType); + return true; + } + else + { + SetToken(JsonToken.PropertyName, ReadElement()); + return true; + } + } + else if (context.Position == lengthMinusEnd) + { + if (ReadByte() != 0) + throw new JsonReaderException("Unexpected end of object byte value."); + + _stack.RemoveAt(_stack.Count - 1); + if (_stack.Count != 0) + MovePosition(context.Length); + + JsonToken endToken = (context.Type == JTokenType.Object) ? JsonToken.EndObject : JsonToken.EndArray; + SetToken(endToken); + return true; + } + else + { + throw new JsonReaderException("Read past end of current container context."); + } + case State.ConstructorStart: + break; + case State.Constructor: + break; + case State.Error: + break; + case State.Finished: + break; + default: + throw new ArgumentOutOfRangeException(); + } + + return false; + } + private byte ReadByte() { MovePosition(1); @@ -191,46 +332,76 @@ namespace Newtonsoft.Json.Bson SetToken(JsonToken.Float, ReadDouble()); break; case BsonType.String: + case BsonType.Symbol: SetToken(JsonToken.String, ReadLengthString()); break; case BsonType.Object: - break; + { + SetToken(JsonToken.StartObject); + + ContainerContext newContext = new ContainerContext(JTokenType.Object); + _stack.Add(newContext); + newContext.Length = ReadInt32(); + break; + } case BsonType.Array: - break; + { + SetToken(JsonToken.StartArray); + + ContainerContext newContext = new ContainerContext(JTokenType.Array); + _stack.Add(newContext); + newContext.Length = ReadInt32(); + break; + } case BsonType.Binary: SetToken(JsonToken.Bytes, ReadBinary()); break; case BsonType.Undefined: + SetToken(JsonToken.Undefined); break; case BsonType.Oid: + byte[] oid = ReadBytes(12); + SetToken(JsonToken.Bytes, oid); break; case BsonType.Boolean: + bool b = Convert.ToBoolean(ReadByte()); + SetToken(JsonToken.Boolean, b); break; case BsonType.Date: + long ticks = ReadInt64(); + DateTime dateTime = JsonConvert.ConvertJavaScriptTicksToDateTime(ticks); + SetToken(JsonToken.Date, dateTime); break; case BsonType.Null: + SetToken(JsonToken.Null); break; case BsonType.Regex: + string expression = ReadString(); + string modifiers = ReadString(); + + string regex = @"/" + expression + @"/" + modifiers; + SetToken(JsonToken.String, regex); break; case BsonType.Reference: + SetToken(JsonToken.StartObject); + _bsonReaderState = BsonReaderState.ReferenceStart; break; case BsonType.Code: - break; - case BsonType.Symbol: + SetToken(JsonToken.String, ReadLengthString()); break; case BsonType.CodeWScope: + SetToken(JsonToken.StartObject); + _bsonReaderState = BsonReaderState.CodeWScopeStart; break; case BsonType.Integer: SetToken(JsonToken.Integer, (long)ReadInt32()); break; case BsonType.TimeStamp: - break; - case BsonType.MinKey: - break; - case BsonType.MaxKey: + case BsonType.Long: + SetToken(JsonToken.Integer, ReadInt64()); break; default: - throw new ArgumentOutOfRangeException("type"); + throw new ArgumentOutOfRangeException("type", "Unexpected BsonType value: " + type); } } @@ -282,6 +453,12 @@ namespace Newtonsoft.Json.Bson return _reader.ReadInt32(); } + private long ReadInt64() + { + MovePosition(8); + return _reader.ReadInt64(); + } + private BsonType ReadType() { MovePosition(1); diff --git a/Src/Newtonsoft.Json/Bson/BsonType.cs b/Src/Newtonsoft.Json/Bson/BsonType.cs index 5a65c5f4..3c98d588 100644 --- a/Src/Newtonsoft.Json/Bson/BsonType.cs +++ b/Src/Newtonsoft.Json/Bson/BsonType.cs @@ -49,6 +49,7 @@ namespace Newtonsoft.Json.Bson CodeWScope = 15, Integer = 16, TimeStamp = 17, + Long = 18, MinKey = -1, MaxKey = 127 } diff --git a/Src/Newtonsoft.Json/Bson/BsonWriter.cs b/Src/Newtonsoft.Json/Bson/BsonWriter.cs index 2b007fb0..755b135a 100644 --- a/Src/Newtonsoft.Json/Bson/BsonWriter.cs +++ b/Src/Newtonsoft.Json/Bson/BsonWriter.cs @@ -48,10 +48,6 @@ namespace Newtonsoft.Json.Bson public override void Flush() { - if (Top != 0) - throw new JsonWriterException("Cannot flush BsonWriter until JSON is complete."); - - WriteToken(Token); _writer.Flush(); } @@ -88,10 +84,10 @@ namespace Newtonsoft.Json.Bson } break; case JTokenType.Integer: - _writer.Write((int)t); + _writer.Write(Convert.ToInt32(((JValue)t).Value)); break; case JTokenType.Float: - _writer.Write((double)t); + _writer.Write(Convert.ToDouble(((JValue)t).Value)); break; case JTokenType.String: WriteStringWithLength((string)t); @@ -247,45 +243,14 @@ namespace Newtonsoft.Json.Bson } } - public void Write(string str) + protected override void WriteEnd(JsonToken token) { - Byte[] buf = new byte[Encoding.UTF8.GetByteCount(str) + 1]; - buf[buf.Length - 1] = 0; - Encoding.UTF8.GetBytes(str, 0, str.Length, buf, 0); - _writer.Write(buf); - } + base.WriteEnd(token); - public void Write(bool val) - { - if (val) - _writer.Write((byte)1); - else - _writer.Write((byte)0); - } - - public void Write(byte val) - { - _writer.Write(val); - } - - public void Write(byte[] val) - { - _writer.Write(val); - } - - public void Write(int val) - { - _writer.Write(val); - } - - public void Write(long val) - { - _writer.Write(val); - } - - public void Write(double val) - { - _writer.Write(val); + if (Top == 0) + { + WriteToken(Token); + } } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs index 23365779..df4d9b57 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -681,6 +681,7 @@ namespace Newtonsoft.Json.Linq case JsonToken.Float: case JsonToken.Date: case JsonToken.Boolean: + case JsonToken.Bytes: JValue v = new JValue(r.Value); v.SetLineInfo(lineInfo); parent.Add(v); diff --git a/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs b/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs index 108cecd1..394b69c5 100644 --- a/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs @@ -45,18 +45,17 @@ namespace Newtonsoft.Json.Utilities public static byte[] HexToBytes(string hex) { + string fixedHex = hex.Replace("-", string.Empty); + // array to put the result in - byte[] bytes = new byte[hex.Length / 2]; + byte[] bytes = new byte[fixedHex.Length / 2]; // variable to determine shift of high/low nibble int shift = 4; // offset of the current byte in the array int offset = 0; // loop the characters in the string - foreach (char c in hex) + foreach (char c in fixedHex) { - if (c == '-' && shift == 4) - continue; - // get character code in range 0-9, 17-22 // the % 32 handles lower case characters int b = (c - '0') % 32;