From e32b4671c89fde6693d5143947a73220a3e9f3a4 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sun, 20 Jul 2014 18:29:31 +1200 Subject: [PATCH] -Added MetadataPropertyHandling.Ignore --- .../MetadataPropertyHandlingTests.cs | 153 +++++++++++++++++- Src/Newtonsoft.Json/JsonReader.cs | 16 +- Src/Newtonsoft.Json/JsonSerializer.cs | 2 +- .../MetadataPropertyHandling.cs | 5 + .../JsonSerializerInternalReader.cs | 19 ++- 5 files changed, 180 insertions(+), 15 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Serialization/MetadataPropertyHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/MetadataPropertyHandlingTests.cs index 27b3406a..d51545c5 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/MetadataPropertyHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/MetadataPropertyHandlingTests.cs @@ -59,9 +59,9 @@ namespace Newtonsoft.Json.Tests.Serialization object o = JsonConvert.DeserializeObject(json, new JsonSerializerSettings { - TypeNameHandling = TypeNameHandling.All, - // no longer needs to be first - MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead + TypeNameHandling = TypeNameHandling.All, + // no longer needs to be first + MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead }); User u = (User)o; @@ -427,5 +427,152 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual(5, nested[4]); } #endif + + public class MetadataPropertyDisabledTestClass + { + [JsonProperty("$id")] + public string Id { get; set; } + + [JsonProperty("$ref")] + public string Ref { get; set; } + + [JsonProperty("$value")] + public string Value { get; set; } + + [JsonProperty("$values")] + public string Values { get; set; } + + [JsonProperty("$type")] + public string Type { get; set; } + } + + [Test] + public void MetadataPropertyHandlingIgnore() + { + MetadataPropertyDisabledTestClass c1 = new MetadataPropertyDisabledTestClass + { + Id = "Id!", + Ref = "Ref!", + Type = "Type!", + Value = "Value!", + Values = "Values!" + }; + + string json = JsonConvert.SerializeObject(c1, Formatting.Indented); + + Assert.AreEqual(@"{ + ""$id"": ""Id!"", + ""$ref"": ""Ref!"", + ""$value"": ""Value!"", + ""$values"": ""Values!"", + ""$type"": ""Type!"" +}", json); + + MetadataPropertyDisabledTestClass c2 = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + MetadataPropertyHandling = MetadataPropertyHandling.Ignore + }); + + Assert.AreEqual("Id!", c2.Id); + Assert.AreEqual("Ref!", c2.Ref); + Assert.AreEqual("Type!", c2.Type); + Assert.AreEqual("Value!", c2.Value); + Assert.AreEqual("Values!", c2.Values); + } + + [Test] + public void MetadataPropertyHandlingIgnore_EmptyObject() + { + string json = @"{}"; + + MetadataPropertyDisabledTestClass c = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + MetadataPropertyHandling = MetadataPropertyHandling.Ignore + }); + + Assert.AreEqual(null, c.Id); + } + + [Test] + public void PrimitiveType_MetadataPropertyIgnore() + { + Item actual = JsonConvert.DeserializeObject(@"{ + ""SourceTypeID"": ""d8220a4b-75b1-4b7a-8112-b7bdae956a45"", + ""BrokerID"": ""951663c4-924e-4c86-a57a-7ed737501dbd"", + ""Latitude"": 33.657145, + ""Longitude"": -117.766684, + ""TimeStamp"": ""2000-03-01T23:59:59Z"", + ""Payload"": { + ""$type"": ""System.Byte[], mscorlib"", + ""$value"": ""AAECAwQFBgcICQ=="" + } +}", + new JsonSerializerSettings + { + MetadataPropertyHandling = MetadataPropertyHandling.Ignore + }); + + Assert.AreEqual(new Guid("d8220a4b-75b1-4b7a-8112-b7bdae956a45"), actual.SourceTypeID); + Assert.AreEqual(new Guid("951663c4-924e-4c86-a57a-7ed737501dbd"), actual.BrokerID); + JObject o = (JObject)actual.Payload; + Assert.AreEqual("System.Byte[], mscorlib", (string)o["$type"]); + Assert.AreEqual("AAECAwQFBgcICQ==", (string)o["$value"]); + } + + public class ItemWithTypedPayload + { + public double Payload { get; set; } + } + + [Test] + public void PrimitiveType_MetadataPropertyIgnore_WithType() + { + ItemWithTypedPayload actual = JsonConvert.DeserializeObject(@"{ + ""Payload"": { + ""$type"": ""System.Double, mscorlib"", + ""$value"": ""5"" + } +}", + new JsonSerializerSettings()); + + Assert.AreEqual(5d, actual.Payload); + + ExceptionAssert.Throws( + @"Cannot deserialize the current JSON object (e.g. {""name"":""value""}) into type 'System.Double' because the type requires a JSON primitive value (e.g. string, number, boolean, null) to deserialize correctly. +To fix this error either change the JSON to a JSON primitive value (e.g. string, number, boolean, null) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. +Path 'Payload.$type', line 3, position 13.", + () => + { + JsonConvert.DeserializeObject(@"{ + ""Payload"": { + ""$type"": ""System.Double, mscorlib"", + ""$value"": ""5"" + } +}", + new JsonSerializerSettings + { + MetadataPropertyHandling = MetadataPropertyHandling.Ignore + }); + }); + } + + [Test] + public void DeserializeCircularReferencesWithConverter() + { + string json = @"{ + ""$id"": ""1"", + ""$type"": ""CircularReferenceClass"" +}"; + + MetadataPropertyDisabledTestClass c = new MetadataPropertyDisabledTestClass(); + + JsonConvert.PopulateObject(json, c, new JsonSerializerSettings + { + MetadataPropertyHandling = MetadataPropertyHandling.Ignore + }); + + Assert.AreEqual("1", c.Id); + Assert.AreEqual("CircularReferenceClass", c.Type); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index 62a675d7..cdd61a6b 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -505,20 +505,22 @@ namespace Newtonsoft.Json { string s = (string)Value; - if (s.Length == 0) - return new byte[0]; - byte[] data; Guid g; - if (ConvertUtils.TryConvertGuid(s, out g)) + if (s.Length == 0) + { + data = new byte[0]; + } + else if (ConvertUtils.TryConvertGuid(s, out g)) { data = g.ToByteArray(); - SetToken(JsonToken.Bytes, data, false); - return data; + } + else + { + data = Convert.FromBase64String(s); } - data = Convert.FromBase64String(s); SetToken(JsonToken.Bytes, data, false); return data; } diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index e5d37583..bcf6ba12 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -266,7 +266,7 @@ namespace Newtonsoft.Json get { return _metadataPropertyHandling; } set { - if (value < MetadataPropertyHandling.Default || value > MetadataPropertyHandling.ReadAhead) + if (value < MetadataPropertyHandling.Default || value > MetadataPropertyHandling.Ignore) throw new ArgumentOutOfRangeException("value"); _metadataPropertyHandling = value; diff --git a/Src/Newtonsoft.Json/MetadataPropertyHandling.cs b/Src/Newtonsoft.Json/MetadataPropertyHandling.cs index ddb9dd7b..5bdacd87 100644 --- a/Src/Newtonsoft.Json/MetadataPropertyHandling.cs +++ b/Src/Newtonsoft.Json/MetadataPropertyHandling.cs @@ -43,5 +43,10 @@ namespace Newtonsoft.Json /// Read metadata properties located anywhere in a JSON object. Note that this setting will impact performance. /// ReadAhead = 1, + + /// + /// Do not try to read metadata properties. + /// + Ignore = 2 } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index d5019736..67befe3c 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -96,7 +96,9 @@ namespace Newtonsoft.Json.Serialization CheckedRead(reader); string id = null; - if (reader.TokenType == JsonToken.PropertyName && string.Equals(reader.Value.ToString(), JsonTypeReflector.IdPropertyName, StringComparison.Ordinal)) + if (Serializer.MetadataPropertyHandling != MetadataPropertyHandling.Ignore + && reader.TokenType == JsonToken.PropertyName + && string.Equals(reader.Value.ToString(), JsonTypeReflector.IdPropertyName, StringComparison.Ordinal)) { CheckedRead(reader); id = (reader.Value != null) ? reader.Value.ToString() : null; @@ -380,10 +382,15 @@ namespace Newtonsoft.Json.Serialization private object CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, object existingValue) { string id; - object newValue; Type resolvedObjectType = objectType; - if (Serializer.MetadataPropertyHandling == MetadataPropertyHandling.ReadAhead) + if (Serializer.MetadataPropertyHandling == MetadataPropertyHandling.Ignore) + { + // don't look for metadata properties + CheckedRead(reader); + id = null; + } + else if (Serializer.MetadataPropertyHandling == MetadataPropertyHandling.ReadAhead) { var tokenReader = reader as JTokenReader; if (tokenReader == null) @@ -403,12 +410,14 @@ namespace Newtonsoft.Json.Serialization reader = tokenReader; } + object newValue; if (ReadMetadataPropertiesToken(tokenReader, ref resolvedObjectType, ref contract, member, containerContract, containerMember, existingValue, out newValue, out id)) return newValue; } else { CheckedRead(reader); + object newValue; if (ReadMetadataProperties(reader, ref resolvedObjectType, ref contract, member, containerContract, containerMember, existingValue, out newValue, out id)) return newValue; } @@ -439,7 +448,9 @@ namespace Newtonsoft.Json.Serialization { JsonPrimitiveContract primitiveContract = (JsonPrimitiveContract)contract; // if the content is inside $value then read past it - if (reader.TokenType == JsonToken.PropertyName && string.Equals(reader.Value.ToString(), JsonTypeReflector.ValuePropertyName, StringComparison.Ordinal)) + if (Serializer.MetadataPropertyHandling != MetadataPropertyHandling.Ignore + && reader.TokenType == JsonToken.PropertyName + && string.Equals(reader.Value.ToString(), JsonTypeReflector.ValuePropertyName, StringComparison.Ordinal)) { CheckedRead(reader);