From e3cec969ef0c8fde1bcf2ed2a81b5803306dcbf9 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sun, 8 Apr 2012 19:33:09 +1200 Subject: [PATCH] -Added support for Serializable attribute and MemberSerialization.Fields -Added support for ignoring properties with NonSerializable attribute -Added MaxDepth to JsonReader/JsonSerializer/JsonSerializerSettings --- .../JsonTextReaderTest.cs | 39 ++++ .../Newtonsoft.Json.Tests.Net20.csproj | 1 + .../Newtonsoft.Json.Tests.Net35.csproj | 1 + .../Newtonsoft.Json.Tests.Silverlight.csproj | 1 + .../Newtonsoft.Json.Tests.WindowsPhone.csproj | 1 + .../Newtonsoft.Json.Tests.csproj | 1 + .../Properties/AssemblyInfo.cs | 2 +- .../Schema/JsonSchemaGeneratorTests.cs | 106 ++++++++--- .../Serialization/JsonSerializerTest.cs | 166 +++++++++++++++++- .../Serialization/WebApiIntegrationTests.cs | 123 +++++++++++++ Src/Newtonsoft.Json/JsonReader.cs | 42 ++--- Src/Newtonsoft.Json/JsonSerializer.cs | 25 +++ Src/Newtonsoft.Json/JsonSerializerSettings.cs | 15 ++ Src/Newtonsoft.Json/MemberSerialization.cs | 18 +- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/DefaultContractResolver.cs | 122 +++++++++---- .../Serialization/JsonSerializerProxy.cs | 6 + .../Serialization/JsonTypeReflector.cs | 42 +++-- .../Utilities/DynamicProxyMetaObject.cs | 10 +- .../Utilities/ReflectionUtils.cs | 15 ++ 20 files changed, 620 insertions(+), 118 deletions(-) create mode 100644 Src/Newtonsoft.Json.Tests/Serialization/WebApiIntegrationTests.cs diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs index 633cc189..bfb7967f 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs @@ -44,6 +44,25 @@ namespace Newtonsoft.Json.Tests [TestFixture] public class JsonTextReaderTest : TestFixtureBase { + [Test] + public void SurrogatePairValid() + { + string json = @"{ ""MATHEMATICAL ITALIC CAPITAL ALPHA"": ""\uD835\uDEE2"" }"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + + Assert.IsTrue(reader.Read()); + Assert.IsTrue(reader.Read()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + + string s = reader.Value.ToString(); + + StringInfo stringInfo = new StringInfo(s); + Assert.AreEqual(1, stringInfo.LengthInTextElements); + } + [Test] public void CloseInput() { @@ -2195,6 +2214,26 @@ bye", reader.Value); reader.Read(); } + [Test] + public void MaxDepth() + { + string json = "[[]]"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)) + { + MaxDepth = 1 + }; + + Assert.IsTrue(reader.Read()); + + ExceptionAssert.Throws( + "This reader's MaxDepth of 1 has been exceeded.", + () => + { + Assert.IsTrue(reader.Read()); + }); + } + [Test] public void ReadingFromSlowStream() { diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj index 3851977e..e624f1d4 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj @@ -94,6 +94,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj index 55fb1c5e..90b6c872 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj @@ -139,6 +139,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj index abe844fa..aa1f60a6 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj @@ -139,6 +139,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj index abd2f67a..8e4aea26 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj @@ -102,6 +102,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index 42564652..c5decf04 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -144,6 +144,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 3f57af2d..dd024d2c 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -72,5 +72,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.5.1.14719")] +[assembly: AssemblyFileVersion("4.5.1.14808")] #endif diff --git a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs index 6530f3af..93378013 100644 --- a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs +++ b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs @@ -256,27 +256,6 @@ namespace Newtonsoft.Json.Tests.Schema Assert.AreEqual(typeof(Store).AssemblyQualifiedName, schema.Id); } - [Test] - public void Generate_NumberFormatInfo() - { - JsonSchemaGenerator generator = new JsonSchemaGenerator(); - JsonSchema schema = generator.Generate(typeof(NumberFormatInfo)); - - string json = schema.ToString(); - - Console.WriteLine(json); - - // Assert.AreEqual(@"{ - // ""type"": ""object"", - // ""additionalProperties"": { - // ""type"": ""array"", - // ""items"": { - // ""type"": ""string"" - // } - // } - //}", json); - } - [Test] public void CircularReferenceError() { @@ -389,7 +368,12 @@ namespace Newtonsoft.Json.Tests.Schema { JsonSchemaGenerator generator = new JsonSchemaGenerator(); generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; - generator.ContractResolver = new CustomDirectoryInfoMapper(); + generator.ContractResolver = new CustomDirectoryInfoMapper + { +#if !(SILVERLIGHT || NETFX_CORE) + IgnoreSerializableAttribute = true +#endif + }; JsonSchema schema = generator.Generate(typeof(DirectoryInfo), true); @@ -468,7 +452,12 @@ namespace Newtonsoft.Json.Tests.Schema JTokenWriter jsonWriter = new JTokenWriter(); JsonSerializer serializer = new JsonSerializer(); serializer.Converters.Add(new IsoDateTimeConverter()); - serializer.ContractResolver = new CustomDirectoryInfoMapper(); + serializer.ContractResolver = new CustomDirectoryInfoMapper + { +#if !(SILVERLIGHT || NETFX_CORE) + IgnoreSerializableInterface = true +#endif + }; serializer.Serialize(jsonWriter, temp); List errors = new List(); @@ -483,9 +472,14 @@ namespace Newtonsoft.Json.Tests.Schema { JsonSchemaGenerator generator = new JsonSchemaGenerator(); generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; - generator.ContractResolver = new CamelCasePropertyNamesContractResolver(); + generator.ContractResolver = new CamelCasePropertyNamesContractResolver() + { +#if !(SILVERLIGHT || NETFX_CORE) + IgnoreSerializableAttribute = true +#endif + }; - JsonSchema schema = generator.Generate(typeof (Version), true); + JsonSchema schema = generator.Generate(typeof(Version), true); string json = schema.ToString(); @@ -525,6 +519,68 @@ namespace Newtonsoft.Json.Tests.Schema }", json); } +#if !SILVERLIGHT + [Test] + public void GenerateSchemaSerializable() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + + JsonSchema schema = generator.Generate(typeof (Version), true); + + string json = schema.ToString(); + + Assert.AreEqual(@"{ + ""id"": ""System.Version"", + ""type"": [ + ""object"", + ""null"" + ], + ""additionalProperties"": false, + ""properties"": { + ""_Major"": { + ""required"": true, + ""type"": ""integer"" + }, + ""_Minor"": { + ""required"": true, + ""type"": ""integer"" + }, + ""_Build"": { + ""required"": true, + ""type"": ""integer"" + }, + ""_Revision"": { + ""required"": true, + ""type"": ""integer"" + } + } +}", json); + + JTokenWriter jsonWriter = new JTokenWriter(); + JsonSerializer serializer = new JsonSerializer(); + serializer.Serialize(jsonWriter, new Version(1, 2, 3, 4)); + + List errors = new List(); + jsonWriter.Token.Validate(schema, (sender, args) => errors.Add(args.Message)); + + Assert.AreEqual(0, errors.Count); + + Assert.AreEqual(@"{ + ""_Major"": 1, + ""_Minor"": 2, + ""_Build"": 3, + ""_Revision"": 4 +}", jsonWriter.Token.ToString()); + + Version version = jsonWriter.Token.ToObject(); + Assert.AreEqual(1, version.Major); + Assert.AreEqual(2, version.Minor); + Assert.AreEqual(3, version.Build); + Assert.AreEqual(4, version.Revision); + } +#endif + public enum SortTypeFlag { No = 0, diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index b47b7cfc..d3ef3440 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -77,6 +77,9 @@ using Newtonsoft.Json.Utilities.LinqBridge; #else using System.Linq; #endif +#if !(SILVERLIGHT || NETFX_CORE) +using System.Drawing; +#endif namespace Newtonsoft.Json.Tests.Serialization { @@ -490,8 +493,6 @@ keyword such as type of business."" new DateTime(2000, 1, 1, 1, 1, 1, DateTimeKind.Unspecified), new DateTime(2000, 1, 1, 1, 1, 1, DateTimeKind.Utc), }; - string result; - MemoryStream ms = new MemoryStream(); DataContractJsonSerializer s = new DataContractJsonSerializer(typeof (List)); @@ -501,7 +502,7 @@ keyword such as type of business."" string expected = sr.ReadToEnd(); - result = JsonConvert.SerializeObject(testDates, new JsonSerializerSettings { DateFormatHandling = DateFormatHandling.MicrosoftDateFormat }); + string result = JsonConvert.SerializeObject(testDates, new JsonSerializerSettings { DateFormatHandling = DateFormatHandling.MicrosoftDateFormat }); Assert.AreEqual(expected, result); } @@ -1582,8 +1583,10 @@ keyword such as type of business."" } }"; - DictionaryInterfaceClass c = JsonConvert.DeserializeObject(json, - new JsonSerializerSettings {ObjectCreationHandling = ObjectCreationHandling.Replace}); + DictionaryInterfaceClass c = JsonConvert.DeserializeObject( + json, + new JsonSerializerSettings {ObjectCreationHandling = ObjectCreationHandling.Replace}); + Assert.AreEqual("Name!", c.Name); Assert.AreEqual(1, c.Dictionary.Count); Assert.AreEqual(11, c.Dictionary["Item"]); @@ -2029,7 +2032,15 @@ To force JSON objects to deserialize add the JsonObjectAttribute to the type. Li )] public void SerializePropertyGetError() { - JsonConvert.SerializeObject(new MemoryStream()); + JsonConvert.SerializeObject(new MemoryStream(), new JsonSerializerSettings + { + ContractResolver = new DefaultContractResolver + { +#if !(SILVERLIGHT || NETFX_CORE) + IgnoreSerializableAttribute = true +#endif + } + }); } [Test] @@ -2040,7 +2051,15 @@ To force JSON objects to deserialize add the JsonObjectAttribute to the type. Li )] public void DeserializePropertySetError() { - JsonConvert.DeserializeObject("{ReadTimeout:0}"); + JsonConvert.DeserializeObject("{ReadTimeout:0}", new JsonSerializerSettings + { + ContractResolver = new DefaultContractResolver + { +#if !(SILVERLIGHT || NETFX_CORE) + IgnoreSerializableAttribute = true +#endif + } + }); } [Test] @@ -2051,7 +2070,15 @@ To force JSON objects to deserialize add the JsonObjectAttribute to the type. Li )] public void DeserializeEnsureTypeEmptyStringToIntError() { - JsonConvert.DeserializeObject("{ReadTimeout:''}"); + JsonConvert.DeserializeObject("{ReadTimeout:''}", new JsonSerializerSettings + { + ContractResolver = new DefaultContractResolver + { +#if !(SILVERLIGHT || NETFX_CORE) + IgnoreSerializableAttribute = true +#endif + } + }); } [Test] @@ -2062,7 +2089,15 @@ To force JSON objects to deserialize add the JsonObjectAttribute to the type. Li )] public void DeserializeEnsureTypeNullToIntError() { - JsonConvert.DeserializeObject("{ReadTimeout:null}"); + JsonConvert.DeserializeObject("{ReadTimeout:null}", new JsonSerializerSettings + { + ContractResolver = new DefaultContractResolver + { +#if !(SILVERLIGHT || NETFX_CORE) + IgnoreSerializableAttribute = true +#endif + } + }); } [Test] @@ -5785,6 +5820,119 @@ Parameter name: value" Assert.AreEqual(person.Name, roundtrippedPerson.Name); } #endif + +#if !(SILVERLIGHT || NETFX_CORE) + [Test] + public void MetroBlogPost() + { + Product product = new Product(); + product.Name = "Apple"; + product.ExpiryDate = new DateTime(2012, 4, 1); + product.Price = 3.99M; + product.Sizes = new []{"Small", "Medium", "Large"}; + + string json = JsonConvert.SerializeObject(product); + //{ + // "Name": "Apple", + // "ExpiryDate": "2012-04-01T00:00:00", + // "Price": 3.99, + // "Sizes": [ "Small", "Medium", "Large" ] + //} + + string metroJson = JsonConvert.SerializeObject(product, new JsonSerializerSettings + { + ContractResolver = new MetroPropertyNameResolver(), + Converters = { new MetroStringConverter() }, + Formatting = Formatting.Indented + }); + Assert.AreEqual(metroJson, @"{ + "":::NAME:::"": "":::APPLE:::"", + "":::EXPIRYDATE:::"": ""2012-04-01T00:00:00"", + "":::PRICE:::"": 3.99, + "":::SIZES:::"": [ + "":::SMALL:::"", + "":::MEDIUM:::"", + "":::LARGE:::"" + ] +}"); + //{ + // ":::NAME:::": ":::APPLE:::", + // ":::EXPIRYDATE:::": "2012-04-01T00:00:00", + // ":::PRICE:::": 3.99, + // ":::SIZES:::": [ ":::SMALL:::", ":::MEDIUM:::", ":::LARGE:::" ] + //} + + Color[] colors = new []{ Color.Blue, Color.Red, Color.Yellow, Color.Green, Color.Black, Color.Brown }; + + string json1 = JsonConvert.SerializeObject(colors); + //[":::GRAY:::", ":::GRAY:::", ":::GRAY:::", ":::GRAY:::", ":::BLACK:::", ":::GRAY:::"] + + Assert.AreEqual(json1, @"[""Blue"",""Red"",""Yellow"",""Green"",""Black"",""Brown""]"); + + string json2 = JsonConvert.SerializeObject(colors, new JsonSerializerSettings + { + ContractResolver = new MetroPropertyNameResolver(), + Converters = { new MetroStringConverter(), new MetroColorConverter() }, + Formatting = Formatting.Indented + }); + + Assert.AreEqual(json2, @"[ + "":::GRAY:::"", + "":::GRAY:::"", + "":::GRAY:::"", + "":::GRAY:::"", + "":::BLACK:::"", + "":::GRAY:::"" +]"); + } + + public class MetroStringConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(":::" + value.ToString().ToUpper() + ":::"); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return existingValue; + } + + public override bool CanConvert(Type objectType) + { + return objectType == typeof (string); + } + } + + public class MetroPropertyNameResolver : DefaultContractResolver + { + protected internal override string ResolvePropertyName(string propertyName) + { + return ":::" + propertyName.ToUpper() + ":::"; + } + } + + public class MetroColorConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + Color color = (Color) value; + Color fixedColor = (color == Color.White || color == Color.Black) ? color : Color.Gray; + + writer.WriteValue(":::" + fixedColor.ToKnownColor().ToString().ToUpper() + ":::"); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return Enum.Parse(typeof (Color), reader.Value.ToString()); + } + + public override bool CanConvert(Type objectType) + { + return objectType == typeof (Color); + } + } +#endif } public class PersonWithPrivateConstructor diff --git a/Src/Newtonsoft.Json.Tests/Serialization/WebApiIntegrationTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/WebApiIntegrationTests.cs new file mode 100644 index 00000000..7e0ea8e1 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Serialization/WebApiIntegrationTests.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.IO; +#if !NET20 +using System.Linq; +#endif +using System.Text; +using NUnit.Framework; +#if !(SILVERLIGHT || NETFX_CORE || NET20) +using System.Runtime.Serialization.Json; +#endif + +namespace Newtonsoft.Json.Tests.Serialization +{ + [TestFixture] + public class WebApiIntegrationTests : TestFixtureBase + { + [Test] + public void SerializeSerializableType() + { + SerializableType serializableType = new SerializableType("protected") + { + publicField = "public", + protectedInternalField = "protected internal", + internalField = "internal", + PublicProperty = "private", + nonSerializedField = "Error" + }; + +#if !(SILVERLIGHT || NETFX_CORE || NET20) + MemoryStream ms = new MemoryStream(); + DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(SerializableType)); + dataContractJsonSerializer.WriteObject(ms, serializableType); + + string dtJson = Encoding.UTF8.GetString(ms.ToArray()); + string dtExpected = @"{""internalField"":""internal"",""privateField"":""private"",""protectedField"":""protected"",""protectedInternalField"":""protected internal"",""publicField"":""public""}"; + + Assert.AreEqual(dtExpected, dtJson); +#endif + + string expected = "{\"publicField\":\"public\",\"internalField\":\"internal\",\"protectedInternalField\":\"protected internal\",\"protectedField\":\"protected\",\"privateField\":\"private\"}"; + string json = JsonConvert.SerializeObject(serializableType); + + Assert.AreEqual(expected, json); + } + +#if !SILVERLIGHT + [Test] + public void SerializeInheritedType() + { + InheritedType serializableType = new InheritedType("protected") + { + publicField = "public", + protectedInternalField = "protected internal", + internalField = "internal", + PublicProperty = "private", + nonSerializedField = "Error", + inheritedTypeField = "inherited" + }; + + string json = JsonConvert.SerializeObject(serializableType); + + Assert.AreEqual(@"{""inheritedTypeField"":""inherited"",""publicField"":""public"",""PublicProperty"":""private""}", json); + } +#endif + } + + public class InheritedType : SerializableType + { + public string inheritedTypeField; + + public InheritedType(string protectedFieldValue) : base(protectedFieldValue) + { + } + } + +#if !(SILVERLIGHT || NETFX_CORE) + [Serializable] +#else + [JsonObject(MemberSerialization.Fields)] +#endif + public class SerializableType : IEquatable + { + public SerializableType(string protectedFieldValue) + { + this.protectedField = protectedFieldValue; + } + + public string publicField; + internal string internalField; + protected internal string protectedInternalField; + protected string protectedField; + private string privateField; + + public string PublicProperty + { + get + { + return privateField; + } + set + { + this.privateField = value; + } + } + +#if !(SILVERLIGHT || NETFX_CORE) + [NonSerialized] +#else + [JsonIgnore] +#endif + public string nonSerializedField; + + public bool Equals(SerializableType other) + { + return this.publicField == other.publicField && + this.internalField == other.internalField && + this.protectedInternalField == other.protectedInternalField && + this.protectedField == other.protectedField && + this.privateField == other.privateField; + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index 71bc5d86..24a36782 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -109,6 +109,7 @@ namespace Newtonsoft.Json private JsonPosition _currentPosition; private CultureInfo _culture; private DateTimeZoneHandling _dateTimeZoneHandling; + private int? _maxDepth; /// /// Gets the current reader state. @@ -149,6 +150,18 @@ namespace Newtonsoft.Json set { _dateTimeZoneHandling = value; } } + public int? MaxDepth + { + get { return _maxDepth; } + set + { + if (value <= 0) + throw new ArgumentException("Value must be positive.", "value"); + + _maxDepth = value; + } + } + /// /// Gets the type of the current JSON token. /// @@ -235,11 +248,15 @@ namespace Newtonsoft.Json else { _stack.Add(_currentPosition); - var state = new JsonPosition + JsonPosition state = new JsonPosition { Type = value }; _currentPosition = state; + + // this is a little hacky because Depth increases when first property/value is written but only testing here is faster/simpler + if (_maxDepth != null && Depth + 1 > _maxDepth) + throw new JsonReaderException("This reader's MaxDepth of {0} has been exceeded.".FormatWith(CultureInfo.InvariantCulture, _maxDepth)); } } @@ -667,7 +684,10 @@ namespace Newtonsoft.Json /// The value. protected void SetToken(JsonToken newToken, object value) { + JsonToken previousToken = _tokenType; + _tokenType = newToken; + _value = value; switch (newToken) { @@ -711,8 +731,6 @@ namespace Newtonsoft.Json UpdateScopeWithFinishedValue(); break; } - - _value = value; } private void UpdateScopeWithFinishedValue() @@ -789,24 +807,8 @@ namespace Newtonsoft.Json case JsonToken.StartArray: case JsonToken.StartConstructor: return true; - case JsonToken.PropertyName: - case JsonToken.None: - case JsonToken.Comment: - case JsonToken.Integer: - case JsonToken.Float: - case JsonToken.String: - case JsonToken.Boolean: - case JsonToken.Null: - case JsonToken.Undefined: - case JsonToken.EndObject: - case JsonToken.EndArray: - case JsonToken.EndConstructor: - case JsonToken.Date: - case JsonToken.Raw: - case JsonToken.Bytes: - return false; default: - throw MiscellaneousUtils.CreateArgumentOutOfRangeException("token", token, "Unexpected JsonToken value."); + return false; } } diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index 7d7efbbc..77990576 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -61,6 +61,8 @@ namespace Newtonsoft.Json private DateFormatHandling? _dateFormatHandling; private DateTimeZoneHandling? _dateTimeZoneHandling; private CultureInfo _culture; + private int? _maxDepth; + private bool _maxDepthSet; /// /// Occurs when the errors during serialization and deserialization. @@ -320,6 +322,19 @@ namespace Newtonsoft.Json get { return _culture ?? JsonSerializerSettings.DefaultCulture; } set { _culture = value; } } + + public virtual int? MaxDepth + { + get { return _maxDepth; } + set + { + if (value <= 0) + throw new ArgumentException("Value must be positive.", "value"); + + _maxDepth = value; + _maxDepthSet = true; + } + } #endregion /// @@ -371,6 +386,8 @@ namespace Newtonsoft.Json jsonSerializer._dateFormatHandling = settings._dateFormatHandling; jsonSerializer._dateTimeZoneHandling = settings._dateTimeZoneHandling; jsonSerializer._culture = settings._culture; + jsonSerializer._maxDepth = settings._maxDepth; + jsonSerializer._maxDepthSet = settings._maxDepthSet; if (settings.Error != null) jsonSerializer.Error += settings.Error; @@ -478,6 +495,12 @@ namespace Newtonsoft.Json previousDateTimeZoneHandling = reader.DateTimeZoneHandling; reader.DateTimeZoneHandling = _dateTimeZoneHandling.Value; } + int? previousMaxDepth = null; + if (_maxDepthSet && reader.MaxDepth != _maxDepth) + { + previousMaxDepth = reader.MaxDepth; + reader.MaxDepth = _maxDepth; + } JsonSerializerInternalReader serializerReader = new JsonSerializerInternalReader(this); object value = serializerReader.Deserialize(reader, objectType); @@ -487,6 +510,8 @@ namespace Newtonsoft.Json reader.Culture = previousCulture; if (previousDateTimeZoneHandling != null) reader.DateTimeZoneHandling = previousDateTimeZoneHandling.Value; + if (_maxDepthSet) + reader.MaxDepth = previousMaxDepth; return value; } diff --git a/Src/Newtonsoft.Json/JsonSerializerSettings.cs b/Src/Newtonsoft.Json/JsonSerializerSettings.cs index bd4228bf..e37251c6 100644 --- a/Src/Newtonsoft.Json/JsonSerializerSettings.cs +++ b/Src/Newtonsoft.Json/JsonSerializerSettings.cs @@ -32,6 +32,8 @@ namespace Newtonsoft.Json internal DateFormatHandling? _dateFormatHandling; internal DateTimeZoneHandling? _dateTimeZoneHandling; internal CultureInfo _culture; + internal int? _maxDepth; + internal bool _maxDepthSet; /// /// Gets or sets how reference loops (e.g. a class referencing itself) is handled. @@ -124,6 +126,19 @@ namespace Newtonsoft.Json /// The context. public StreamingContext Context { get; set; } + public int? MaxDepth + { + get { return _maxDepth; } + set + { + if (value <= 0) + throw new ArgumentException("Value must be positive.", "value"); + + _maxDepth = value; + _maxDepthSet = true; + } + } + /// /// Indicates how JSON text output is formatted. /// diff --git a/Src/Newtonsoft.Json/MemberSerialization.cs b/Src/Newtonsoft.Json/MemberSerialization.cs index 86b546f0..0ec5823c 100644 --- a/Src/Newtonsoft.Json/MemberSerialization.cs +++ b/Src/Newtonsoft.Json/MemberSerialization.cs @@ -23,6 +23,9 @@ // OTHER DEALINGS IN THE SOFTWARE. #endregion +using System; +using System.Runtime.Serialization; + namespace Newtonsoft.Json { /// @@ -31,12 +34,19 @@ namespace Newtonsoft.Json public enum MemberSerialization { /// - /// All members are serialized by default. Members can be excluded using the . + /// All public members are serialized by default. Members can be excluded using or . + /// This is the default member serialization mode. /// OptOut, /// - /// Only members must be marked with the are serialized. + /// Only members must be marked with or are serialized. + /// This member serialization mode can also be set by marking the class with . /// - OptIn + OptIn, + /// + /// All public and private fields are serialized. Members can be excluded using or . + /// This member serialization mode can also be set by marking the class with . + /// + Fields } -} +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 99d1f056..b418ee13 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -88,7 +88,7 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.5.1.14719")] +[assembly: AssemblyFileVersion("4.5.1.14808")] #endif [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index ce65e215..0636be60 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -151,7 +151,7 @@ namespace Newtonsoft.Json.Serialization /// public bool SerializeCompilerGeneratedMembers { get; set; } -#if !SILVERLIGHT && !PocketPC && !NETFX_CORE +#if !(SILVERLIGHT || NETFX_CORE) /// /// Gets or sets a value indicating whether to ignore the ISerializable interface when serializing and deserializing types. /// @@ -159,6 +159,8 @@ namespace Newtonsoft.Json.Serialization /// true if the ISerializable interface will be ignored when serializing and deserializing types; otherwise, false. /// public bool IgnoreSerializableInterface { get; set; } + + public bool IgnoreSerializableAttribute { get; set; } #endif /// @@ -243,45 +245,71 @@ namespace Newtonsoft.Json.Serialization /// The serializable members for the type. protected virtual List GetSerializableMembers(Type objectType) { -#if !PocketPC && !NET20 - DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(objectType); + bool ignoreSerializableAttribute; +#if !(SILVERLIGHT || NETFX_CORE) + ignoreSerializableAttribute = IgnoreSerializableAttribute; +#else + ignoreSerializableAttribute = true; #endif - List defaultMembers = ReflectionUtils.GetFieldsAndProperties(objectType, DefaultMembersSearchFlags) - .Where(m => !ReflectionUtils.IsIndexedProperty(m)).ToList(); + MemberSerialization memberSerialization = JsonTypeReflector.GetObjectMemberSerialization(objectType, ignoreSerializableAttribute); + List allMembers = ReflectionUtils.GetFieldsAndProperties(objectType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static) .Where(m => !ReflectionUtils.IsIndexedProperty(m)).ToList(); List serializableMembers = new List(); - foreach (MemberInfo member in allMembers) + + if (memberSerialization != MemberSerialization.Fields) { - // exclude members that are compiler generated if set - if (SerializeCompilerGeneratedMembers || !member.IsDefined(typeof(CompilerGeneratedAttribute), true)) - { - if (defaultMembers.Contains(member)) - { - // add all members that are found by default member search - serializableMembers.Add(member); - } - else - { - // add members that are explicitly marked with JsonProperty/DataMember attribute - if (JsonTypeReflector.GetAttribute(member.GetCustomAttributeProvider()) != null) - serializableMembers.Add(member); #if !PocketPC && !NET20 - else if (dataContractAttribute != null && JsonTypeReflector.GetAttribute(member.GetCustomAttributeProvider()) != null) - serializableMembers.Add(member); + DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(objectType); #endif + + List defaultMembers = ReflectionUtils.GetFieldsAndProperties(objectType, DefaultMembersSearchFlags) + .Where(m => !ReflectionUtils.IsIndexedProperty(m)).ToList(); + + foreach (MemberInfo member in allMembers) + { + // exclude members that are compiler generated if set + if (SerializeCompilerGeneratedMembers || !member.IsDefined(typeof (CompilerGeneratedAttribute), true)) + { + if (defaultMembers.Contains(member)) + { + // add all members that are found by default member search + serializableMembers.Add(member); + } + else + { + // add members that are explicitly marked with JsonProperty/DataMember attribute + // or are a field if serializing just fields + if (JsonTypeReflector.GetAttribute(member.GetCustomAttributeProvider()) != null) + serializableMembers.Add(member); +#if !PocketPC && !NET20 + else if (dataContractAttribute != null && JsonTypeReflector.GetAttribute(member.GetCustomAttributeProvider()) != null) + serializableMembers.Add(member); +#endif + else if (memberSerialization == MemberSerialization.Fields && member.MemberType() == MemberTypes.Field) + serializableMembers.Add(member); + } } } - } #if !PocketPC && !SILVERLIGHT && !NET20 - Type match; - // don't include EntityKey on entities objects... this is a bit hacky - if (objectType.AssignableToTypeName("System.Data.Objects.DataClasses.EntityObject", out match)) - serializableMembers = serializableMembers.Where(ShouldSerializeEntityMember).ToList(); + Type match; + // don't include EntityKey on entities objects... this is a bit hacky + if (objectType.AssignableToTypeName("System.Data.Objects.DataClasses.EntityObject", out match)) + serializableMembers = serializableMembers.Where(ShouldSerializeEntityMember).ToList(); #endif + } + else + { + // serialize all fields + foreach (MemberInfo member in allMembers) + { + if (member.MemberType() == MemberTypes.Field) + serializableMembers.Add(member); + } + } return serializableMembers; } @@ -310,7 +338,14 @@ namespace Newtonsoft.Json.Serialization JsonObjectContract contract = new JsonObjectContract(objectType); InitializeContract(contract); - contract.MemberSerialization = JsonTypeReflector.GetObjectMemberSerialization(contract.NonNullableUnderlyingType); + bool ignoreSerializableAttribute; +#if !(SILVERLIGHT || NETFX_CORE) + ignoreSerializableAttribute = IgnoreSerializableAttribute; +#else + ignoreSerializableAttribute = true; +#endif + + contract.MemberSerialization = JsonTypeReflector.GetObjectMemberSerialization(contract.NonNullableUnderlyingType, ignoreSerializableAttribute); contract.Properties.AddRange(CreateProperties(contract.NonNullableUnderlyingType, contract.MemberSerialization)); // check if a JsonConstructorAttribute has been defined and use that @@ -872,7 +907,7 @@ namespace Newtonsoft.Json.Serialization DataMemberAttribute dataMemberAttribute; if (dataContractAttribute != null && memberInfo != null) - dataMemberAttribute = JsonTypeReflector.GetDataMemberAttribute((MemberInfo)memberInfo); + dataMemberAttribute = JsonTypeReflector.GetDataMemberAttribute((MemberInfo) memberInfo); else dataMemberAttribute = null; #endif @@ -881,8 +916,6 @@ namespace Newtonsoft.Json.Serialization if (propertyAttribute != null) hasExplicitAttribute = true; - bool hasIgnoreAttribute = (JsonTypeReflector.GetAttribute(attributeProvider) != null); - string mappedName; if (propertyAttribute != null && propertyAttribute.PropertyName != null) mappedName = propertyAttribute.PropertyName; @@ -913,13 +946,28 @@ namespace Newtonsoft.Json.Serialization property.Required = Required.Default; } - property.Ignored = (hasIgnoreAttribute || - (memberSerialization == MemberSerialization.OptIn - && propertyAttribute == null -#if !PocketPC && !NET20 - && dataMemberAttribute == null + bool hasJsonIgnoreAttribute = JsonTypeReflector.GetAttribute(attributeProvider) != null +#if !(SILVERLIGHT || NETFX_CORE) + || JsonTypeReflector.GetAttribute(attributeProvider) != null #endif -)); + ; + + if (memberSerialization != MemberSerialization.OptIn) + { + // ignored if it has JsonIgnore or NonSerialized attributes + property.Ignored = hasJsonIgnoreAttribute; + } + else + { + // ignored if it has JsonIgnore/NonSerialized or does not have DataMember or JsonProperty attributes + property.Ignored = + hasJsonIgnoreAttribute + || (propertyAttribute == null +#if !PocketPC && !NET20 + && dataMemberAttribute == null +#endif + ); + } // resolve converter for property // the class type might have a converter but the property converter takes presidence @@ -941,6 +989,8 @@ namespace Newtonsoft.Json.Serialization allowNonPublicAccess = true; if (propertyAttribute != null) allowNonPublicAccess = true; + if (memberSerialization == MemberSerialization.Fields) + allowNonPublicAccess = true; #if !PocketPC && !NET20 if (dataMemberAttribute != null) diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs index 0e5a7065..07761f5a 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs @@ -150,6 +150,12 @@ namespace Newtonsoft.Json.Serialization set { _serializer.Culture = value; } } + public override int? MaxDepth + { + get { return _serializer.MaxDepth; } + set { _serializer.MaxDepth = value; } + } + internal JsonSerializerInternalBase GetInternalSerializer() { if (_serializerReader != null) diff --git a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs index f1b07b9c..00b85bec 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs @@ -102,19 +102,29 @@ namespace Newtonsoft.Json.Serialization return GetJsonContainerAttribute(type) as JsonArrayAttribute; } +#if !(SILVERLIGHT || NETFX_CORE) + public static SerializableAttribute GetSerializableAttribute(Type type) + { + return CachedAttributeGetter.GetAttribute(type.GetCustomAttributeProvider()); + } +#endif + #if !PocketPC && !NET20 public static DataContractAttribute GetDataContractAttribute(Type type) { // DataContractAttribute does not have inheritance - DataContractAttribute result = null; Type currentType = type; - while (result == null && currentType != null) + + while (currentType != null) { - result = CachedAttributeGetter.GetAttribute(currentType.GetCustomAttributeProvider()); + DataContractAttribute result = CachedAttributeGetter.GetAttribute(currentType.GetCustomAttributeProvider()); + if (result != null) + return result; + currentType = currentType.BaseType(); } - return result; + return null; } public static DataMemberAttribute GetDataMemberAttribute(MemberInfo memberInfo) @@ -149,23 +159,29 @@ namespace Newtonsoft.Json.Serialization } #endif - public static MemberSerialization GetObjectMemberSerialization(Type objectType) + public static MemberSerialization GetObjectMemberSerialization(Type objectType, bool ignoreSerializableAttribute) { JsonObjectAttribute objectAttribute = GetJsonObjectAttribute(objectType); + if (objectAttribute != null) + return objectAttribute.MemberSerialization; - if (objectAttribute == null) - { #if !PocketPC && !NET20 - DataContractAttribute dataContractAttribute = GetDataContractAttribute(objectType); - - if (dataContractAttribute != null) - return MemberSerialization.OptIn; + DataContractAttribute dataContractAttribute = GetDataContractAttribute(objectType); + if (dataContractAttribute != null) + return MemberSerialization.OptIn; #endif - return MemberSerialization.OptOut; +#if !(SILVERLIGHT || NETFX_CORE) + if (!ignoreSerializableAttribute) + { + SerializableAttribute serializableAttribute = GetSerializableAttribute(objectType); + if (serializableAttribute != null) + return MemberSerialization.Fields; } +#endif - return objectAttribute.MemberSerialization; + // the default + return MemberSerialization.OptOut; } private static Type GetJsonConverterType(ICustomAttributeProvider attributeProvider) diff --git a/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs b/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs index da807411..8530c05d 100644 --- a/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs +++ b/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs @@ -24,15 +24,7 @@ namespace Newtonsoft.Json.Utilities private bool IsOverridden(string method) { - return _proxy.GetType().GetMember(method, MemberTypes.Method, BindingFlags.Public | BindingFlags.Instance).Cast() - .Any(info => - // check that the method overrides the original on DynamicObjectProxy - info.DeclaringType != typeof(DynamicProxy) - // todo - find out whether there is a way to do this in winrt -#if !NETFX_CORE - && info.GetBaseDefinition().DeclaringType == typeof(DynamicProxy) -#endif - ); + return ReflectionUtils.IsMethodOverridden(_proxy.GetType(), typeof (DynamicProxy), method); } public override DynamicMetaObject BindGetMember(GetMemberBinder binder) diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index bd2f9e70..c676cc94 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -978,5 +978,20 @@ namespace Newtonsoft.Json.Utilities } } } + + public static bool IsMethodOverridden(Type currentType, Type methodDeclaringType, string method) + { + bool isMethodOverriden = currentType.GetMember(method, MemberTypes.Method, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Cast() + .Any(info => + // check that the method overrides the original on DynamicObjectProxy + info.DeclaringType != methodDeclaringType + // todo - find out whether there is a way to do this in winrt +#if !NETFX_CORE + && info.GetBaseDefinition().DeclaringType == methodDeclaringType +#endif + ); + + return isMethodOverriden; + } } } \ No newline at end of file