diff --git a/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs b/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs index fb179ccc..21b615bd 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs @@ -185,7 +185,7 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual("", d.Null.ToString()); Assert.AreEqual("1", d.Integer.ToString()); - Assert.AreEqual("1.1", d.Float.ToString()); + Assert.AreEqual("1.1", d.Float.ToString(CultureInfo.InvariantCulture)); Assert.AreEqual("12/29/2000 23:51:10", d.DateTime.ToString(null, CultureInfo.InvariantCulture)); Assert.AreEqual("True", d.Boolean.ToString()); Assert.AreEqual("A string lol!", d.String.ToString()); diff --git a/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs b/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs index ec8d63d3..73fae2b4 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs @@ -25,6 +25,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text; using NUnit.Framework; @@ -285,7 +286,7 @@ keyword such as type of business."" 1264118400000 )", o.Property("Establised").Value.ToString()); Assert.AreEqual(@"""Width"": 1.1", o.Property("Width").ToString()); - Assert.AreEqual(@"1.1", o.Property("Width").Value.ToString()); + Assert.AreEqual(@"1.1", ((JValue)o.Property("Width").Value).ToString(CultureInfo.InvariantCulture)); Assert.AreEqual(@"""Open"": false", o.Property("Open").ToString()); Assert.AreEqual(@"False", o.Property("Open").Value.ToString()); diff --git a/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs index 02ac0a48..a13fdf94 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs @@ -182,6 +182,103 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual("Child text!", n.DynamicChildObject.Text); Assert.AreEqual(int.MinValue, n.DynamicChildObject.Integer); } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Unable to find a default constructor to use for type System.Dynamic.DynamicObject.")] + public void NoPublicDefaultConstructor() + { + var settings = new JsonSerializerSettings(); + settings.NullValueHandling = NullValueHandling.Ignore; + var json = @"{ + ""contributors"": null +}"; + + JsonConvert.DeserializeObject(json, settings); + } + + public class DictionaryDynamicObject : DynamicObject + { + public IDictionary Values { get; private set; } + + protected DictionaryDynamicObject() + { + Values = new Dictionary(); + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + Values[binder.Name] = value; + return true; + } + } + + [Test] + public void AllowNonPublicDefaultConstructor() + { + var settings = new JsonSerializerSettings(); + settings.ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor; + + var json = @"{ + ""contributors"": null, + ""retweeted"": false, + ""text"": ""Guys SX4 diesel is launched.what are your plans?catch us at #facebook http://bit.ly/dV3H1a #auto #car #maruti #india #delhi"", + ""in_reply_to_user_id_str"": null, + ""retweet_count"": 0, + ""geo"": null, + ""id_str"": ""40678260320768000"", + ""in_reply_to_status_id"": null, + ""source"": ""TweetDeck"", + ""created_at"": ""Thu Feb 24 07:43:47 +0000 2011"", + ""place"": null, + ""coordinates"": null, + ""truncated"": false, + ""favorited"": false, + ""user"": { + ""profile_background_image_url"": ""http://a1.twimg.com/profile_background_images/206944715/twitter_bg.jpg"", + ""url"": ""http://bit.ly/dcFwWC"", + ""screen_name"": ""marutisuzukisx4"", + ""verified"": false, + ""friends_count"": 45, + ""description"": ""This is the Official Maruti Suzuki SX4 Twitter ID! Men are Back - mail us on social (at) sx4bymaruti (dot) com"", + ""follow_request_sent"": null, + ""time_zone"": ""Chennai"", + ""profile_text_color"": ""333333"", + ""location"": ""India"", + ""notifications"": null, + ""profile_sidebar_fill_color"": ""efefef"", + ""id_str"": ""196143889"", + ""contributors_enabled"": false, + ""lang"": ""en"", + ""profile_background_tile"": false, + ""created_at"": ""Tue Sep 28 12:55:15 +0000 2010"", + ""followers_count"": 117, + ""show_all_inline_media"": true, + ""listed_count"": 1, + ""geo_enabled"": true, + ""profile_link_color"": ""009999"", + ""profile_sidebar_border_color"": ""eeeeee"", + ""protected"": false, + ""name"": ""Maruti Suzuki SX4"", + ""statuses_count"": 637, + ""following"": null, + ""profile_use_background_image"": true, + ""profile_image_url"": ""http://a3.twimg.com/profile_images/1170694644/Slide1_normal.JPG"", + ""id"": 196143889, + ""is_translator"": false, + ""utc_offset"": 19800, + ""favourites_count"": 0, + ""profile_background_color"": ""131516"" + }, + ""in_reply_to_screen_name"": null, + ""id"": 40678260320768000, + ""in_reply_to_status_id_str"": null, + ""in_reply_to_user_id"": null +}"; + + DictionaryDynamicObject foo = JsonConvert.DeserializeObject(json, settings); + + Assert.AreEqual(false, foo.Values["retweeted"]); + } } } #endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index e2502b66..172bfbb8 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -28,6 +28,7 @@ using System.Collections.Generic; #if !SILVERLIGHT && !PocketPC && !NET20 using System.ComponentModel.DataAnnotations; using System.Runtime.CompilerServices; +using System.Threading; using System.Web.Script.Serialization; #endif using System.Text; @@ -4178,5 +4179,29 @@ keyword such as type of business."" ""Name"": null }", json); } + + public class DeserializeStringConvert + { + public string Name { get; set; } + public int Age { get; set; } + public double Height { get; set; } + public decimal Price { get; set; } + } + + [Test] + public void DeserializeStringEnglish() + { + string json = @"{ + 'Name': 'James Hughes', + 'Age': '40', + 'Height': '44.4', + 'Price': '4' +}"; + + DeserializeStringConvert p = JsonConvert.DeserializeObject(json); + Assert.AreEqual(40, p.Age); + Assert.AreEqual(44.4, p.Height); + Assert.AreEqual(4d, p.Price); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs index 5c43fa26..416a3c7e 100644 --- a/Src/Newtonsoft.Json/JsonTextReader.cs +++ b/Src/Newtonsoft.Json/JsonTextReader.cs @@ -348,6 +348,13 @@ namespace Newtonsoft.Json if (TokenType == JsonToken.Float) return (decimal?)Value; + decimal d; + if (TokenType == JsonToken.String && decimal.TryParse((string)Value, NumberStyles.Number, CultureInfo.InvariantCulture, out d)) + { + SetToken(JsonToken.Float, d); + return d; + } + throw CreateJsonReaderException("Unexpected token when reading decimal: {0}. Line {1}, position {2}.", TokenType, _currentLineNumber, _currentLinePosition); } diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index 9f89a624..4b05a58f 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -458,6 +458,30 @@ namespace Newtonsoft.Json.Linq return _value.ToString(); } + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + return ToString(format, CultureInfo.CurrentCulture); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return ToString(null, formatProvider); + } + /// /// Returns a that represents this instance. /// diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 5747d9e9..044a17e7 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -228,7 +228,7 @@ namespace Newtonsoft.Json.Serialization case JsonToken.Boolean: case JsonToken.Date: case JsonToken.Bytes: - return EnsureType(reader.Value, objectType); + return EnsureType(reader.Value, CultureInfo.InvariantCulture, objectType); case JsonToken.String: // convert empty string to null automatically for nullable types if (string.IsNullOrEmpty((string)reader.Value) && @@ -240,7 +240,7 @@ namespace Newtonsoft.Json.Serialization if (objectType == typeof(byte[])) return Convert.FromBase64String((string)reader.Value); - return EnsureType(reader.Value, objectType); + return EnsureType(reader.Value, CultureInfo.InvariantCulture, objectType); case JsonToken.StartConstructor: case JsonToken.EndConstructor: string constructorName = reader.Value.ToString(); @@ -251,7 +251,7 @@ namespace Newtonsoft.Json.Serialization if (objectType == typeof (DBNull)) return DBNull.Value; - return EnsureType(reader.Value, objectType); + return EnsureType(reader.Value, CultureInfo.InvariantCulture, objectType); case JsonToken.Raw: return new JRaw((string)reader.Value); case JsonToken.Comment: @@ -463,7 +463,7 @@ namespace Newtonsoft.Json.Serialization ); } - private object EnsureType(object value, Type targetType) + private object EnsureType(object value, CultureInfo culture, Type targetType) { if (targetType == null) return value; @@ -476,7 +476,7 @@ namespace Newtonsoft.Json.Serialization { try { - return ConvertUtils.ConvertOrCast(value, CultureInfo.InvariantCulture, targetType); + return ConvertUtils.ConvertOrCast(value, culture, targetType); } catch (Exception ex) { @@ -612,7 +612,7 @@ namespace Newtonsoft.Json.Serialization object keyValue; try { - keyValue = EnsureType(reader.Value, contract.DictionaryKeyType); + keyValue = EnsureType(reader.Value, CultureInfo.InvariantCulture, contract.DictionaryKeyType); } catch (Exception ex) { @@ -776,9 +776,9 @@ namespace Newtonsoft.Json.Serialization if (contract.DefaultCreator != null && (!contract.DefaultCreatorNonPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor)) - { newObject = (IDynamicMetaObjectProvider)contract.DefaultCreator(); - } + else + throw new JsonSerializationException("Unable to find a default constructor to use for type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); if (id != null) Serializer.ReferenceResolver.AddReference(id, newObject); diff --git a/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs b/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs index ca5b3308..9d853fdd 100644 --- a/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs @@ -98,6 +98,8 @@ namespace Newtonsoft.Json.Utilities public static bool TryGetMember(this IDynamicMetaObjectProvider dynamicProvider, string name, out object value) { + ValidationUtils.ArgumentNotNull(dynamicProvider, "dynamicProvider"); + GetMemberBinder getMemberBinder = (GetMemberBinder) BinderWrapper.GetMember(name, typeof (DynamicUtils)); CallSite> callSite = CallSite>.Create(new NoThrowGetBinderMember(getMemberBinder)); @@ -118,7 +120,9 @@ namespace Newtonsoft.Json.Utilities public static bool TrySetMember(this IDynamicMetaObjectProvider dynamicProvider, string name, object value) { - SetMemberBinder binder = (SetMemberBinder) BinderWrapper.SetMember(name, value.GetType()); + ValidationUtils.ArgumentNotNull(dynamicProvider, "dynamicProvider"); + + SetMemberBinder binder = (SetMemberBinder)BinderWrapper.SetMember(name, typeof(DynamicUtils)); var setterSite = CallSite>.Create(new NoThrowSetBinderMember(binder));