diff --git a/Src/Newtonsoft.Json.Tests/Bson/BsonReaderTests.cs b/Src/Newtonsoft.Json.Tests/Bson/BsonReaderTests.cs index e0246bca..3af89e97 100644 --- a/Src/Newtonsoft.Json.Tests/Bson/BsonReaderTests.cs +++ b/Src/Newtonsoft.Json.Tests/Bson/BsonReaderTests.cs @@ -24,6 +24,7 @@ #endregion using System; +using System.Collections.Generic; using System.Globalization; #if !(NET20 || NET35 || SILVERLIGHT || PORTABLE) using System.Numerics; @@ -94,6 +95,76 @@ namespace Newtonsoft.Json.Tests.Bson Assert.AreEqual(JsonToken.None, reader.TokenType); } + [Test] + public void ReadGuid_Text() + { + byte[] data = HexToBytes("31-00-00-00-02-30-00-25-00-00-00-64-38-32-31-65-65-64-37-2D-34-62-35-63-2D-34-33-63-39-2D-38-61-63-32-2D-36-39-32-38-65-35-37-39-62-37-30-35-00-00"); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + reader.ReadRootValueAsArray = true; + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("d821eed7-4b5c-43c9-8ac2-6928e579b705", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsFalse(reader.Read()); + Assert.AreEqual(JsonToken.None, reader.TokenType); + + ms = new MemoryStream(data); + reader = new BsonReader(ms); + reader.ReadRootValueAsArray = true; + + JsonSerializer serializer = new JsonSerializer(); + IList l = serializer.Deserialize>(reader); + + Assert.AreEqual(1, l.Count); + Assert.AreEqual(new Guid("D821EED7-4B5C-43C9-8AC2-6928E579B705"), l[0]); + } + + [Test] + public void ReadGuid_Bytes() + { + byte[] data = HexToBytes("1D-00-00-00-05-30-00-10-00-00-00-04-D7-EE-21-D8-5C-4B-C9-43-8A-C2-69-28-E5-79-B7-05-00"); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + reader.ReadRootValueAsArray = true; + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Guid g = new Guid("D821EED7-4B5C-43C9-8AC2-6928E579B705"); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Bytes, reader.TokenType); + CollectionAssert.AreEqual(g.ToByteArray(), (byte[])reader.Value); + Assert.AreEqual(typeof(byte[]), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsFalse(reader.Read()); + Assert.AreEqual(JsonToken.None, reader.TokenType); + + ms = new MemoryStream(data); + reader = new BsonReader(ms); + reader.ReadRootValueAsArray = true; + + JsonSerializer serializer = new JsonSerializer(); + IList l = serializer.Deserialize>(reader); + + Assert.AreEqual(1, l.Count); + Assert.AreEqual(g, l[0]); + } + [Test] public void ReadDouble() { diff --git a/Src/Newtonsoft.Json.Tests/Bson/BsonWriterTests.cs b/Src/Newtonsoft.Json.Tests/Bson/BsonWriterTests.cs index 2c3af63c..9b411dc2 100644 --- a/Src/Newtonsoft.Json.Tests/Bson/BsonWriterTests.cs +++ b/Src/Newtonsoft.Json.Tests/Bson/BsonWriterTests.cs @@ -128,6 +128,23 @@ namespace Newtonsoft.Json.Tests.Bson Assert.AreEqual("10-00-00-00-01-30-00-8F-C2-F5-28-5C-FF-58-40-00", bson); } + [Test] + public void WriteGuid() + { + Guid g = new Guid("D821EED7-4B5C-43C9-8AC2-6928E579B705"); + + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + writer.WriteStartArray(); + writer.WriteValue(g); + writer.WriteEnd(); + + string bson = BytesToHex(ms.ToArray()); + Console.WriteLine(bson); + Assert.AreEqual("1D-00-00-00-05-30-00-10-00-00-00-04-D7-EE-21-D8-5C-4B-C9-43-8A-C2-69-28-E5-79-B7-05-00", bson); + } + [Test] public void WriteArrayBsonFromSite() { diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs index 1221ee31..6d92d3ac 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs @@ -319,6 +319,8 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual(null, (Uri)(new JValue((object)null))); Assert.AreEqual(Convert.ToBase64String(Encoding.UTF8.GetBytes("hi")), (string)(new JValue(Encoding.UTF8.GetBytes("hi")))); CollectionAssert.AreEquivalent((byte[])Encoding.UTF8.GetBytes("hi"), (byte[])(new JValue(Convert.ToBase64String(Encoding.UTF8.GetBytes("hi"))))); + Assert.AreEqual(new Guid("46EFE013-B56A-4E83-99E4-4DCE7678A5BC"), (Guid)(new JValue(new Guid("46EFE013-B56A-4E83-99E4-4DCE7678A5BC").ToByteArray()))); + Assert.AreEqual(new Guid("46EFE013-B56A-4E83-99E4-4DCE7678A5BC"), (Guid?)(new JValue(new Guid("46EFE013-B56A-4E83-99E4-4DCE7678A5BC").ToByteArray()))); Assert.AreEqual(null, (Uri)(JValue)null); Assert.AreEqual(null, (int?)(JValue)null); diff --git a/Src/Newtonsoft.Json/Bson/BsonBinaryType.cs b/Src/Newtonsoft.Json/Bson/BsonBinaryType.cs index 08264466..d4977ac5 100644 --- a/Src/Newtonsoft.Json/Bson/BsonBinaryType.cs +++ b/Src/Newtonsoft.Json/Bson/BsonBinaryType.cs @@ -32,8 +32,10 @@ namespace Newtonsoft.Json.Bson Binary = 0x00, Function = 0x01, [Obsolete("This type has been deprecated in the BSON specification. Use Binary instead.")] - Data = 0x02, - Uuid = 0x03, + BinaryOld = 0x02, + [Obsolete("This type has been deprecated in the BSON specification. Use Uuid instead.")] + UuidOld = 0x03, + Uuid = 0x04, Md5 = 0x05, UserDefined = 0x80 } diff --git a/Src/Newtonsoft.Json/Bson/BsonBinaryWriter.cs b/Src/Newtonsoft.Json/Bson/BsonBinaryWriter.cs index aa931a5b..65908a3e 100644 --- a/Src/Newtonsoft.Json/Bson/BsonBinaryWriter.cs +++ b/Src/Newtonsoft.Json/Bson/BsonBinaryWriter.cs @@ -161,11 +161,11 @@ namespace Newtonsoft.Json.Bson break; case BsonType.Binary: { - BsonValue value = (BsonValue)t; + BsonBinary value = (BsonBinary)t; byte[] data = (byte[])value.Value; _writer.Write(data.Length); - _writer.Write((byte)BsonBinaryType.Binary); + _writer.Write((byte)value.BinaryType); _writer.Write(data); } break; @@ -298,7 +298,7 @@ namespace Newtonsoft.Json.Bson return 8; case BsonType.Binary: { - BsonValue value = (BsonValue)t; + BsonBinary value = (BsonBinary)t; byte[] data = (byte[])value.Value; value.CalculatedSize = 4 + 1 + data.Length; diff --git a/Src/Newtonsoft.Json/Bson/BsonReader.cs b/Src/Newtonsoft.Json/Bson/BsonReader.cs index 2d73bc72..8d1e6e27 100644 --- a/Src/Newtonsoft.Json/Bson/BsonReader.cs +++ b/Src/Newtonsoft.Json/Bson/BsonReader.cs @@ -89,6 +89,7 @@ namespace Newtonsoft.Json.Bson /// /// true if binary data reading will be compatible with incorrect Json.NET 3.5 written binary; otherwise, false. /// + [Obsolete("JsonNet35BinaryCompatibility will be removed in a future version of Json.NET.")] public bool JsonNet35BinaryCompatibility { get { return _jsonNet35BinaryCompatibility; } @@ -608,7 +609,7 @@ namespace Newtonsoft.Json.Bson #pragma warning disable 612,618 // the old binary type has the data length repeated in the data for some reason - if (binaryType == BsonBinaryType.Data && !_jsonNet35BinaryCompatibility) + if (binaryType == BsonBinaryType.BinaryOld && !_jsonNet35BinaryCompatibility) { dataLength = ReadInt32(); } diff --git a/Src/Newtonsoft.Json/Bson/BsonToken.cs b/Src/Newtonsoft.Json/Bson/BsonToken.cs index 97a98aea..601d0588 100644 --- a/Src/Newtonsoft.Json/Bson/BsonToken.cs +++ b/Src/Newtonsoft.Json/Bson/BsonToken.cs @@ -121,6 +121,17 @@ namespace Newtonsoft.Json.Bson } } + internal class BsonBinary : BsonValue + { + public BsonBinaryType BinaryType { get; set; } + + public BsonBinary(byte[] value, BsonBinaryType binaryType) + : base(value, BsonType.Binary) + { + BinaryType = binaryType; + } + } + internal class BsonRegex : BsonToken { public BsonString Pattern { get; set; } diff --git a/Src/Newtonsoft.Json/Bson/BsonType.cs b/Src/Newtonsoft.Json/Bson/BsonType.cs index f4e12149..73361e9d 100644 --- a/Src/Newtonsoft.Json/Bson/BsonType.cs +++ b/Src/Newtonsoft.Json/Bson/BsonType.cs @@ -25,7 +25,7 @@ namespace Newtonsoft.Json.Bson { - internal enum BsonType : sbyte + internal enum BsonType : sbyte { Number = 1, String = 2, diff --git a/Src/Newtonsoft.Json/Bson/BsonWriter.cs b/Src/Newtonsoft.Json/Bson/BsonWriter.cs index 0ef86b64..93de99fd 100644 --- a/Src/Newtonsoft.Json/Bson/BsonWriter.cs +++ b/Src/Newtonsoft.Json/Bson/BsonWriter.cs @@ -429,7 +429,7 @@ namespace Newtonsoft.Json.Bson public override void WriteValue(byte[] value) { base.WriteValue(value); - AddValue(value, BsonType.Binary); + AddToken(new BsonBinary(value, BsonBinaryType.Binary)); } /// @@ -439,7 +439,7 @@ namespace Newtonsoft.Json.Bson public override void WriteValue(Guid value) { base.WriteValue(value); - AddToken(new BsonString(value.ToString(), true)); + AddToken(new BsonBinary(value.ToByteArray(), BsonBinaryType.Uuid)); } /// @@ -470,7 +470,7 @@ namespace Newtonsoft.Json.Bson public override void WriteValue(BigInteger value) { base.WriteValue(value); - AddToken(new BsonValue(value.ToByteArray(), BsonType.Binary)); + AddToken(new BsonBinary(value.ToByteArray(), BsonBinaryType.Binary)); } #endif #endregion diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 1aa8f5df..d58fc55a 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -69,7 +69,7 @@ namespace Newtonsoft.Json.Linq private static readonly JTokenType[] NumberTypes = new[] { JTokenType.Integer, JTokenType.Float, JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Boolean }; private static readonly JTokenType[] BigIntegerTypes = new[] { JTokenType.Integer, JTokenType.Float, JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Boolean, JTokenType.Bytes }; private static readonly JTokenType[] StringTypes = new[] { JTokenType.Date, JTokenType.Integer, JTokenType.Float, JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Boolean, JTokenType.Bytes, JTokenType.Guid, JTokenType.TimeSpan, JTokenType.Uri }; - private static readonly JTokenType[] GuidTypes = new[] { JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Guid }; + private static readonly JTokenType[] GuidTypes = new[] { JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Guid, JTokenType.Bytes }; private static readonly JTokenType[] TimeSpanTypes = new[] { JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.TimeSpan }; private static readonly JTokenType[] UriTypes = new[] { JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Uri }; private static readonly JTokenType[] CharTypes = new[] { JTokenType.Integer, JTokenType.Float, JTokenType.String, JTokenType.Comment, JTokenType.Raw }; @@ -1093,7 +1093,10 @@ namespace Newtonsoft.Json.Linq if (v == null || !ValidateToken(v, GuidTypes, false)) throw new ArgumentException("Can not convert {0} to Guid.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); - return (v.Value is Guid) ? (Guid)v.Value : new Guid(Convert.ToString(v.Value, CultureInfo.InvariantCulture)); + if (v.Value is byte[]) + return new Guid((byte[]) v.Value); + + return (v.Value is Guid) ? (Guid) v.Value : new Guid(Convert.ToString(v.Value, CultureInfo.InvariantCulture)); } /// @@ -1113,6 +1116,9 @@ namespace Newtonsoft.Json.Linq if (v.Value == null) return null; + if (v.Value is byte[]) + return new Guid((byte[])v.Value); + return (v.Value is Guid) ? (Guid)v.Value : new Guid(Convert.ToString(v.Value, CultureInfo.InvariantCulture)); } diff --git a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs index 61d43bf8..da127ad1 100644 --- a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs @@ -371,17 +371,14 @@ namespace Newtonsoft.Json.Utilities return System.Convert.ChangeType(initialValue, targetType, culture); } - if (initialValue is string && typeof(Type).IsAssignableFrom(targetType)) - return Type.GetType((string) initialValue, true); - - if (targetType.IsInterface() || targetType.IsGenericTypeDefinition() || targetType.IsAbstract()) - throw new ArgumentException("Target type {0} is not a value type or a non-abstract class.".FormatWith(CultureInfo.InvariantCulture, targetType), "targetType"); - #if !NET20 if (initialValue is DateTime && targetType == typeof(DateTimeOffset)) return new DateTimeOffset((DateTime)initialValue); #endif + if (initialValue is byte[] && targetType == typeof(Guid)) + return new Guid((byte[]) initialValue); + if (initialValue is string) { if (targetType == typeof (Guid)) @@ -390,6 +387,8 @@ namespace Newtonsoft.Json.Utilities return new Uri((string) initialValue, UriKind.RelativeOrAbsolute); if (targetType == typeof(TimeSpan)) return ParseTimeSpan((string) initialValue); + if (typeof(Type).IsAssignableFrom(targetType)) + return Type.GetType((string) initialValue, true); } #if !(NET20 || NET35 || SILVERLIGHT || PORTABLE40 || PORTABLE) @@ -399,6 +398,9 @@ namespace Newtonsoft.Json.Utilities return FromBigInteger((BigInteger)initialValue, targetType); #endif + if (targetType.IsInterface() || targetType.IsGenericTypeDefinition() || targetType.IsAbstract()) + throw new ArgumentException("Target type {0} is not a value type or a non-abstract class.".FormatWith(CultureInfo.InvariantCulture, targetType), "targetType"); + #if !(NETFX_CORE || PORTABLE40 || PORTABLE) // see if source or target types have a TypeConverter that converts between the two TypeConverter toConverter = GetConverter(initialType);