diff --git a/Src/Newtonsoft.Json.Tests/Converters/BinaryConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/BinaryConverterTests.cs new file mode 100644 index 00000000..daf2c921 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Converters/BinaryConverterTests.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +#if !SILVERLIGHT && !PocketPC +using System.Data.Linq; +#endif +#if !SILVERLIGHT +using System.Data.SqlTypes; +#endif +using System.Linq; +using System.Text; +using Newtonsoft.Json.Converters; +using NUnit.Framework; +using Newtonsoft.Json.Tests.TestObjects; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class BinaryConverterTests : TestFixtureBase + { + private static readonly byte[] TestData = Encoding.UTF8.GetBytes("This is some test data!!!"); + + public class ByteArrayClass + { + public byte[] ByteArray { get; set; } + public byte[] NullByteArray { get; set; } + } + +#if !SILVERLIGHT && !PocketPC + [Test] + public void DeserializeBinaryClass() + { + string json = @"{ + ""Binary"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullBinary"": null +}"; + + BinaryClass binaryClass = JsonConvert.DeserializeObject(json, new BinaryConverter()); + + Assert.AreEqual(new Binary(TestData), binaryClass.Binary); + Assert.AreEqual(null, binaryClass.NullBinary); + } + + public class BinaryClass + { + public Binary Binary { get; set; } + public Binary NullBinary { get; set; } + } + + [Test] + public void SerializeBinaryClass() + { + BinaryClass binaryClass = new BinaryClass(); + binaryClass.Binary = new Binary(TestData); + binaryClass.NullBinary = null; + + string json = JsonConvert.SerializeObject(binaryClass, Formatting.Indented, new BinaryConverter()); + + Assert.AreEqual(@"{ + ""Binary"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullBinary"": null +}", json); + } +#endif + + [Test] + public void SerializeByteArrayClass() + { + ByteArrayClass byteArrayClass = new ByteArrayClass(); + byteArrayClass.ByteArray = TestData; + byteArrayClass.NullByteArray = null; + + string json = JsonConvert.SerializeObject(byteArrayClass, Formatting.Indented, new BinaryConverter()); + + Assert.AreEqual(@"{ + ""ByteArray"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullByteArray"": null +}", json); + } + +#if !SILVERLIGHT + public class SqlBinaryClass + { + public SqlBinary SqlBinary { get; set; } + public SqlBinary? NullableSqlBinary1 { get; set; } + public SqlBinary? NullableSqlBinary2 { get; set; } + } + + [Test] + public void SerializeSqlBinaryClass() + { + SqlBinaryClass sqlBinaryClass = new SqlBinaryClass(); + sqlBinaryClass.SqlBinary = new SqlBinary(TestData); + sqlBinaryClass.NullableSqlBinary1 = new SqlBinary(TestData); + sqlBinaryClass.NullableSqlBinary2 = null; + + string json = JsonConvert.SerializeObject(sqlBinaryClass, Formatting.Indented, new BinaryConverter()); + + Assert.AreEqual(@"{ + ""SqlBinary"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullableSqlBinary1"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullableSqlBinary2"": null +}", json); + } + + [Test] + public void DeserializeSqlBinaryClass() + { + string json = @"{ + ""SqlBinary"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullableSqlBinary1"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullableSqlBinary2"": null +}"; + + SqlBinaryClass sqlBinaryClass = JsonConvert.DeserializeObject(json, new BinaryConverter()); + + Assert.AreEqual(new SqlBinary(TestData), sqlBinaryClass.SqlBinary); + Assert.AreEqual(new SqlBinary(TestData), sqlBinaryClass.NullableSqlBinary1); + Assert.AreEqual(null, sqlBinaryClass.NullableSqlBinary2); + } +#endif + + [Test] + public void DeserializeByteArrayClass() + { + string json = @"{ + ""ByteArray"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullByteArray"": null +}"; + + ByteArrayClass byteArrayClass = JsonConvert.DeserializeObject(json, new BinaryConverter()); + + Assert.AreEqual(TestData, byteArrayClass.ByteArray); + Assert.AreEqual(null, byteArrayClass.NullByteArray); + } + + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs new file mode 100644 index 00000000..bbe15680 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Converters; +using NUnit.Framework; +using Newtonsoft.Json.Tests.TestObjects; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class StringEnumConverterTests : TestFixtureBase + { + public class EnumClass + { + public StoreColor StoreColor { get; set; } + public StoreColor? NullableStoreColor1 { get; set; } + public StoreColor? NullableStoreColor2 { get; set; } + } + + public enum NegativeEnum + { + Negative = -1, + Zero = 0, + Positive = 1 + } + + public class NegativeEnumClass + { + public NegativeEnum Value1 { get; set; } + public NegativeEnum Value2 { get; set; } + } + + [Test] + public void SerializeEnumClass() + { + EnumClass enumClass = new EnumClass(); + enumClass.StoreColor = StoreColor.Red; + enumClass.NullableStoreColor1 = StoreColor.White; + enumClass.NullableStoreColor2 = null; + + string json = JsonConvert.SerializeObject(enumClass, Formatting.Indented, new StringEnumConverter()); + + Assert.AreEqual(@"{ + ""StoreColor"": ""Red"", + ""NullableStoreColor1"": ""White"", + ""NullableStoreColor2"": null +}", json); + } + + [Test] + public void SerializeEnumClassUndefined() + { + EnumClass enumClass = new EnumClass(); + enumClass.StoreColor = (StoreColor)1000; + enumClass.NullableStoreColor1 = (StoreColor)1000; + enumClass.NullableStoreColor2 = null; + + string json = JsonConvert.SerializeObject(enumClass, Formatting.Indented, new StringEnumConverter()); + + Assert.AreEqual(@"{ + ""StoreColor"": 1000, + ""NullableStoreColor1"": 1000, + ""NullableStoreColor2"": null +}", json); + } + + [Test] + public void SerializeFlagEnum() + { + EnumClass enumClass = new EnumClass(); + enumClass.StoreColor = StoreColor.Red | StoreColor.White; + enumClass.NullableStoreColor1 = StoreColor.White & StoreColor.Yellow; + enumClass.NullableStoreColor2 = StoreColor.Red | StoreColor.White | StoreColor.Black; + + string json = JsonConvert.SerializeObject(enumClass, Formatting.Indented, new StringEnumConverter()); + + Assert.AreEqual(@"{ + ""StoreColor"": ""Red, White"", + ""NullableStoreColor1"": 0, + ""NullableStoreColor2"": ""Black, Red, White"" +}", json); + } + + [Test] + public void SerializeNegativeEnum() + { + NegativeEnumClass negativeEnumClass = new NegativeEnumClass(); + negativeEnumClass.Value1 = NegativeEnum.Negative; + negativeEnumClass.Value2 = (NegativeEnum) int.MinValue; + + string json = JsonConvert.SerializeObject(negativeEnumClass, Formatting.Indented, new StringEnumConverter()); + + Assert.AreEqual(@"{ + ""Value1"": ""Negative"", + ""Value2"": -2147483648 +}", json); + } + + [Test] + public void DeserializeNegativeEnum() + { + string json = @"{ + ""Value1"": ""Negative"", + ""Value2"": -2147483648 +}"; + + NegativeEnumClass negativeEnumClass = JsonConvert.DeserializeObject(json, new StringEnumConverter()); + + Assert.AreEqual(NegativeEnum.Negative, negativeEnumClass.Value1); + Assert.AreEqual((NegativeEnum)int.MinValue, negativeEnumClass.Value2); + } + + [Test] + public void DeserializeFlagEnum() + { + string json = @"{ + ""StoreColor"": ""Red, White"", + ""NullableStoreColor1"": 0, + ""NullableStoreColor2"": ""Black, Red, White"" +}"; + + EnumClass enumClass = JsonConvert.DeserializeObject(json, new StringEnumConverter()); + + Assert.AreEqual(StoreColor.Red | StoreColor.White, enumClass.StoreColor); + Assert.AreEqual((StoreColor)0, enumClass.NullableStoreColor1); + Assert.AreEqual(StoreColor.Red | StoreColor.White | StoreColor.Black, enumClass.NullableStoreColor2); + } + + [Test] + public void DeserializeEnumClass() + { + string json = @"{ + ""StoreColor"": ""Red"", + ""NullableStoreColor1"": ""White"", + ""NullableStoreColor2"": null +}"; + + EnumClass enumClass = JsonConvert.DeserializeObject(json, new StringEnumConverter()); + + Assert.AreEqual(StoreColor.Red, enumClass.StoreColor); + Assert.AreEqual(StoreColor.White, enumClass.NullableStoreColor1); + Assert.AreEqual(null, enumClass.NullableStoreColor2); + } + + [Test] + public void DeserializeEnumClassUndefined() + { + string json = @"{ + ""StoreColor"": 1000, + ""NullableStoreColor1"": 1000, + ""NullableStoreColor2"": null +}"; + + EnumClass enumClass = JsonConvert.DeserializeObject(json, new StringEnumConverter()); + + Assert.AreEqual((StoreColor)1000, enumClass.StoreColor); + Assert.AreEqual((StoreColor)1000, enumClass.NullableStoreColor1); + Assert.AreEqual(null, enumClass.NullableStoreColor2); + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs index 279c9607..e39a7745 100644 --- a/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs @@ -604,7 +604,7 @@ keyword such as type of business."" //JsonConvert.ConvertDateTimeToJavaScriptTicks(s1.Establised.DateTime) - Assert.AreEqual(@"{""Color"":2,""Establised"":""\/Date(1264122061000+0000)\/"",""Width"":1.1,""Employees"":999,""RoomsPerFloor"":[1,2,3,4,5,6,7,8,9],""Open"":false,""Symbol"":""@"",""Mottos"":[""Hello World"",""öäüÖÄÜ\\'{new Date(12345);}[222]_µ@²³~"",null,"" ""],""Cost"":100980.1,""Escape"":""\r\n\t\f\b?{\\r\\n\""'"",""product"":[{""Name"":""Rocket"",""ExpiryDate"":""\/Date(949532490000)\/"",""Price"":0},{""Name"":""Alien"",""ExpiryDate"":""\/Date(946684800000)\/"",""Price"":0}]}", sw.GetStringBuilder().ToString()); + Assert.AreEqual(@"{""Color"":4,""Establised"":""\/Date(1264122061000+0000)\/"",""Width"":1.1,""Employees"":999,""RoomsPerFloor"":[1,2,3,4,5,6,7,8,9],""Open"":false,""Symbol"":""@"",""Mottos"":[""Hello World"",""öäüÖÄÜ\\'{new Date(12345);}[222]_µ@²³~"",null,"" ""],""Cost"":100980.1,""Escape"":""\r\n\t\f\b?{\\r\\n\""'"",""product"":[{""Name"":""Rocket"",""ExpiryDate"":""\/Date(949532490000)\/"",""Price"":0},{""Name"":""Alien"",""ExpiryDate"":""\/Date(946684800000)\/"",""Price"":0}]}", sw.GetStringBuilder().ToString()); Store s2 = (Store)jsonSerializer.Deserialize(new JsonTextReader(new StringReader("{}")), typeof(Store)); Assert.AreEqual("\r\n\t\f\b?{\\r\\n\"\'", s2.Escape); diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj index 6f18e175..b13c426f 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj @@ -58,6 +58,8 @@ + + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj index 42e89cca..68b36de3 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj @@ -63,6 +63,8 @@ + + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index be18826d..6ff647d2 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -57,6 +57,9 @@ 3.5 + + 3.5 + 3.0 @@ -77,6 +80,8 @@ + + diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/StoreColor.cs b/Src/Newtonsoft.Json.Tests/TestObjects/StoreColor.cs index 43cf7fbc..7c064f48 100644 --- a/Src/Newtonsoft.Json.Tests/TestObjects/StoreColor.cs +++ b/Src/Newtonsoft.Json.Tests/TestObjects/StoreColor.cs @@ -23,13 +23,16 @@ // OTHER DEALINGS IN THE SOFTWARE. #endregion +using System; + namespace Newtonsoft.Json.Tests.TestObjects { + [Flags] public enum StoreColor { - Black, - Red, - Yellow, - White + Black = 1, + Red = 2, + Yellow = 4, + White = 8 } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Converters/BinaryConverter.cs b/Src/Newtonsoft.Json/Converters/BinaryConverter.cs new file mode 100644 index 00000000..c62f9487 --- /dev/null +++ b/Src/Newtonsoft.Json/Converters/BinaryConverter.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +#if !SILVERLIGHT && !PocketPC +using System.Data.Linq; +#endif +#if !SILVERLIGHT +using System.Data.SqlTypes; +#endif +using System.Linq; +using System.Text; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a binary value to and from a base 64 string value. + /// + public class BinaryConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + public override void WriteJson(JsonWriter writer, object value) + { + if (value == null) + { + writer.WriteNull(); + return; + } + + byte[] data = value as byte[]; + + if (data == null) + data = GetByteArray(value); + + writer.WriteValue(Convert.ToBase64String(data)); + } + + private byte[] GetByteArray(object value) + { +#if !SILVERLIGHT && !PocketPC + if (value is Binary) + return ((Binary)value).ToArray(); +#endif +#if !SILVERLIGHT + if (value is SqlBinary) + return ((SqlBinary) value).Value; +#endif + throw new Exception("Unexpected value type when writing binary: {0}".FormatWith(CultureInfo.InvariantCulture, value.GetType())); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType) + { + Type t = (ReflectionUtils.IsNullableType(objectType)) + ? Nullable.GetUnderlyingType(objectType) + : objectType; + + if (reader.TokenType == JsonToken.Null) + { + if (!ReflectionUtils.IsNullable(objectType)) + throw new Exception("Cannot convert null value to {0}.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + return null; + } + + if (reader.TokenType != JsonToken.String) + throw new Exception("Unexpected token parsing binary. Expected String, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + + string encodedData = reader.Value.ToString(); + + byte[] data = Convert.FromBase64String(encodedData); + + if (t == typeof(byte[])) + return data; + +#if !SILVERLIGHT && !PocketPC + if (typeof(Binary).IsAssignableFrom(t)) + return new Binary(data); +#endif +#if !SILVERLIGHT + if (typeof(SqlBinary).IsAssignableFrom(t)) + return new SqlBinary(data); +#endif + throw new Exception("Unexpected object type when writing binary: {0}".FormatWith(CultureInfo.InvariantCulture, objectType)); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + Type t = (ReflectionUtils.IsNullableType(objectType)) + ? Nullable.GetUnderlyingType(objectType) + : objectType; + + if (t == typeof(byte[])) + return true; + +#if !SILVERLIGHT && !PocketPC + if (typeof(Binary).IsAssignableFrom(t)) + return true; +#endif +#if !SILVERLIGHT + if (typeof(SqlBinary).IsAssignableFrom(t)) + return true; +#endif + return false; + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Converters/StringEnumConverter.cs b/Src/Newtonsoft.Json/Converters/StringEnumConverter.cs new file mode 100644 index 00000000..19ccc60e --- /dev/null +++ b/Src/Newtonsoft.Json/Converters/StringEnumConverter.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts an to and from its name string value. + /// + public class StringEnumConverter : JsonConverter + { + + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + public override void WriteJson(JsonWriter writer, object value) + { + if (value == null) + { + writer.WriteNull(); + return; + } + + Enum e = (Enum) value; + string enumName = e.ToString("G"); + + if (char.IsNumber(enumName[0]) || enumName[0] == '-') + writer.WriteValue(value); + else + writer.WriteValue(enumName); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType) + { + Type t = (ReflectionUtils.IsNullableType(objectType)) + ? Nullable.GetUnderlyingType(objectType) + : objectType; + + if (reader.TokenType == JsonToken.Null) + { + if (!ReflectionUtils.IsNullableType(objectType)) + throw new Exception("Cannot convert null value to {0}.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + return null; + } + + if (reader.TokenType == JsonToken.String) + return Enum.Parse(t, reader.Value.ToString(), true); + + if (reader.TokenType == JsonToken.Integer) + return ConvertUtils.ConvertOrCast(reader.Value, CultureInfo.InvariantCulture, t); + + throw new Exception("Unexpected token when parsing enum. Expected String or Integer, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + Type t = (ReflectionUtils.IsNullableType(objectType)) + ? Nullable.GetUnderlyingType(objectType) + : objectType; + + return t.IsEnum; + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj index 82166fad..3eb66ae5 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj @@ -55,6 +55,8 @@ + + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index 359508e4..4deeaa35 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -48,9 +48,11 @@ + + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 683d18bb..a9c113bd 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -69,11 +69,16 @@ 3.5 + + 3.5 + + + diff --git a/Src/Newtonsoft.Json/StringBuffer.cs b/Src/Newtonsoft.Json/StringBuffer.cs index 9fa6b0ac..574af4a1 100644 --- a/Src/Newtonsoft.Json/StringBuffer.cs +++ b/Src/Newtonsoft.Json/StringBuffer.cs @@ -24,8 +24,6 @@ #endregion using System; -using System.Collections.Generic; -using System.Text; namespace Newtonsoft.Json { @@ -37,7 +35,7 @@ namespace Newtonsoft.Json private char[] _buffer; private int _position; - private static char[] _emptyBuffer = new char[0]; + private static readonly char[] _emptyBuffer = new char[0]; public int Position {