From 67654ec106dd7332ef89671e409b90784ba6f342 Mon Sep 17 00:00:00 2001 From: JamesNK Date: Thu, 18 Jun 2009 12:23:49 +0000 Subject: [PATCH] -JsonObjectAttribute will now force a collection class to be serialized as an object -Renamed ThreadSafeDictionaryWrapper to ThreadSafeStore --- .../JsonSerializerTest.cs | 74 +++++++++++++++++++ .../Linq/JObjectTests.cs | 29 +++++++- .../Newtonsoft.Json.Compact.csproj | 2 +- .../Newtonsoft.Json.Silverlight.csproj | 2 +- Src/Newtonsoft.Json/Newtonsoft.Json.csproj | 2 +- .../Serialization/CachedAttributeGetter.cs | 2 +- .../Serialization/DefaultMappingResolver.cs | 14 ++-- .../Serialization/JsonMemberMapping.cs | 6 +- .../Serialization/JsonSerializerWriter.cs | 22 ++++-- .../Serialization/JsonTypeReflector.cs | 11 ++- .../ThreadSafeDictionaryWrapper.cs | 62 ---------------- .../Utilities/ThreadSafeStore.cs | 63 ++++++++++++++++ 12 files changed, 201 insertions(+), 88 deletions(-) delete mode 100644 Src/Newtonsoft.Json/Serialization/ThreadSafeDictionaryWrapper.cs create mode 100644 Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs diff --git a/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs index 3c007774..0d786b39 100644 --- a/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs @@ -2623,5 +2623,79 @@ keyword such as type of business."" Assert.AreEqual(@"{""bar"":{""baz"":13}}", json); } + + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class Content : IEnumerable + { + [JsonProperty] + public List Children; + [JsonProperty] + public string Text; + + public IEnumerator GetEnumerator() + { + return Children.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return Children.GetEnumerator(); + } + } + + [Test] + public void SerializeEnumerableAsObject() + { + Content content = new Content + { + Text = "Blah, blah, blah", + Children = new List + { + new Content { Text = "First" }, + new Content { Text = "Second" } + } + }; + + string json = JsonConvert.SerializeObject(content, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Children"": [ + { + ""Children"": null, + ""Text"": ""First"" + }, + { + ""Children"": null, + ""Text"": ""Second"" + } + ], + ""Text"": ""Blah, blah, blah"" +}", json); + } + + [Test] + public void DeserializeEnumerableAsObject() + { + string json = @"{ + ""Children"": [ + { + ""Children"": null, + ""Text"": ""First"" + }, + { + ""Children"": null, + ""Text"": ""Second"" + } + ], + ""Text"": ""Blah, blah, blah"" +}"; + + Content content = JsonConvert.DeserializeObject(json); + + Assert.AreEqual("Blah, blah, blah", content.Text); + Assert.AreEqual(2, content.Children.Count); + Assert.AreEqual("First", content.Children[0].Text); + Assert.AreEqual("Second", content.Children[1].Text); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs index cf9c82ee..bb069655 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs @@ -456,7 +456,7 @@ Parameter name: arrayIndex")] } [Test] - public void sdfs() + public void DeserializeClassManually() { string jsonText = @"{ ""short"":{ @@ -486,5 +486,32 @@ Parameter name: arrayIndex")] Assert.AreEqual(0, shortie.Error.Code); Assert.AreEqual("No action taken", shortie.Error.ErrorMessage); } + + [Test] + public void JObjectContainingHtml() + { + JObject o = new JObject(); + o["rc"] = new JValue(200); + o["m"] = new JValue(""); + o["o"] = new JValue(@"
+
+ asdf
+ 0 +
+
+

+ 444444444 +

+
+
+
+
"); + + Assert.AreEqual(@"{ + ""rc"": 200, + ""m"": """", + ""o"": ""
\r\n
\r\n asdf
\r\n 0\r\n
\r\n
\r\n

\r\n 444444444\r\n

\r\n
\r\n
\r\n
\r\n
"" +}", o.ToString()); + } } } diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj index ac364524..bdc9b44f 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj @@ -135,7 +135,6 @@ - @@ -156,6 +155,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index def157ff..61e5fead 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -113,7 +113,6 @@ - @@ -131,6 +130,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 588350da..4a016ed9 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -161,7 +161,7 @@ - + diff --git a/Src/Newtonsoft.Json/Serialization/CachedAttributeGetter.cs b/Src/Newtonsoft.Json/Serialization/CachedAttributeGetter.cs index b51ca4a8..752d75c2 100644 --- a/Src/Newtonsoft.Json/Serialization/CachedAttributeGetter.cs +++ b/Src/Newtonsoft.Json/Serialization/CachedAttributeGetter.cs @@ -34,7 +34,7 @@ namespace Newtonsoft.Json.Serialization { internal static class CachedAttributeGetter where T : Attribute { - private static readonly ThreadSafeDictionaryWrapper TypeAttributeCache = new ThreadSafeDictionaryWrapper(JsonTypeReflector.GetAttribute); + private static readonly ThreadSafeStore TypeAttributeCache = new ThreadSafeStore(JsonTypeReflector.GetAttribute); public static T GetAttribute(ICustomAttributeProvider type) { diff --git a/Src/Newtonsoft.Json/Serialization/DefaultMappingResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultMappingResolver.cs index 4ef43d5b..41a454f2 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultMappingResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultMappingResolver.cs @@ -39,7 +39,7 @@ namespace Newtonsoft.Json.Serialization { internal static readonly IMappingResolver Instance = new DefaultMappingResolver(); - private readonly ThreadSafeDictionaryWrapper _typeMemberMappingsCache; + private readonly ThreadSafeStore _typeMemberMappingsCache; /// /// Gets or sets the default members search flags. @@ -54,7 +54,7 @@ namespace Newtonsoft.Json.Serialization { DefaultMembersSearchFlags = BindingFlags.Public | BindingFlags.Instance; - _typeMemberMappingsCache = new ThreadSafeDictionaryWrapper(CreateMemberMappings); + _typeMemberMappingsCache = new ThreadSafeStore(CreateMemberMappings); } /// @@ -132,7 +132,7 @@ namespace Newtonsoft.Json.Serialization /// A created for the given . protected virtual JsonMemberMapping CreateMemberMapping(MemberSerialization memberSerialization, MemberInfo member) { - JsonObjectAttribute jsonObjectAttribute = JsonTypeReflector.GetJsonContainerAttribute(member.DeclaringType) as JsonObjectAttribute; + JsonObjectAttribute jsonObjectAttribute = JsonTypeReflector.GetJsonObjectAttribute(member.DeclaringType); #if !PocketPC DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(member.DeclaringType); @@ -188,12 +188,8 @@ namespace Newtonsoft.Json.Serialization NullValueHandling? nullValueHandling = (propertyAttribute != null) ? propertyAttribute._nullValueHandling : null; DefaultValueHandling? defaultValueHandling = (propertyAttribute != null) ? propertyAttribute._defaultValueHandling : null; ReferenceLoopHandling? referenceLoopHandling = (propertyAttribute != null) ? propertyAttribute._referenceLoopHandling : null; - - bool isReference; - if (jsonObjectAttribute != null) - isReference = jsonObjectAttribute.IsReference; - else - isReference = false; + + bool? isReference = (jsonObjectAttribute != null) ? jsonObjectAttribute._isReference : null; return new JsonMemberMapping(resolvedMappedName, member, ignored, readable, writable, memberConverter, defaultValue, required, isReference, nullValueHandling, defaultValueHandling, referenceLoopHandling); } diff --git a/Src/Newtonsoft.Json/Serialization/JsonMemberMapping.cs b/Src/Newtonsoft.Json/Serialization/JsonMemberMapping.cs index 5fed024b..fe5e908e 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonMemberMapping.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonMemberMapping.cs @@ -40,7 +40,7 @@ namespace Newtonsoft.Json.Serialization private readonly JsonConverter _memberConverter; private readonly object _defaultValue; private readonly bool _required; - private readonly bool _isReference; + private readonly bool? _isReference; private readonly NullValueHandling? _nullValueHandling; private readonly DefaultValueHandling? _defaultValueHandling; private readonly ReferenceLoopHandling? _referenceLoopHandling; @@ -60,7 +60,7 @@ namespace Newtonsoft.Json.Serialization /// Mapping null value handling. /// Mapping default value handling. /// Mapping reference loop handling. - public JsonMemberMapping(string propertyName, MemberInfo member, bool ignored, bool readable, bool writable, JsonConverter memberConverter, object defaultValue, bool required, bool isReference, NullValueHandling? nullValueHandling, DefaultValueHandling? defaultValueHandling, ReferenceLoopHandling? referenceLoopHandling) + public JsonMemberMapping(string propertyName, MemberInfo member, bool ignored, bool readable, bool writable, JsonConverter memberConverter, object defaultValue, bool required, bool? isReference, NullValueHandling? nullValueHandling, DefaultValueHandling? defaultValueHandling, ReferenceLoopHandling? referenceLoopHandling) { _propertyName = propertyName; _member = member; @@ -154,7 +154,7 @@ namespace Newtonsoft.Json.Serialization /// /// true if this instance is reference; otherwise, false. /// - public bool IsReference + public bool? IsReference { get { return _isReference; } } diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerWriter.cs index 46ae8330..59e2a14a 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerWriter.cs @@ -97,6 +97,11 @@ namespace Newtonsoft.Json.Serialization { ((JToken)value).WriteTo(writer, (_serializer.Converters != null) ? _serializer.Converters.ToArray() : null); } + else if (JsonTypeReflector.GetJsonObjectAttribute(value.GetType()) != null) + { + // for collection objects that have a JsonObject attribute + SerializeObject(writer, value); + } else if (value is IList) { SerializeList(writer, (IList)value); @@ -119,7 +124,7 @@ namespace Newtonsoft.Json.Serialization } } - private bool ShouldWriteReference(object value) + private bool ShouldWriteReference(object value, JsonMemberMapping mapping) { if (value == null) return false; @@ -128,7 +133,12 @@ namespace Newtonsoft.Json.Serialization Type t = value.GetType(); - bool? isReference = IsReference(t); + // value could be coming from a dictionary or array and not have a mapping + bool? isReference; + if (mapping != null) + isReference = mapping.IsReference; + else + isReference = IsReference(t); if (isReference == null) { @@ -165,7 +175,7 @@ namespace Newtonsoft.Json.Serialization if (memberMapping.DefaultValueHandling.GetValueOrDefault(_serializer.DefaultValueHandling) == DefaultValueHandling.Ignore && Equals(memberValue, defaultValue)) return; - if (ShouldWriteReference(memberValue)) + if (ShouldWriteReference(memberValue, memberMapping)) { writer.WritePropertyName(propertyName ?? member.Name); WriteReference(writer, memberValue); @@ -287,7 +297,7 @@ namespace Newtonsoft.Json.Serialization private void SerializeConvertable(JsonWriter writer, JsonConverter converter, object value) { - if (ShouldWriteReference(value)) + if (ShouldWriteReference(value, null)) { WriteReference(writer, value); } @@ -338,7 +348,7 @@ namespace Newtonsoft.Json.Serialization { object value = values[i]; - if (ShouldWriteReference(value)) + if (ShouldWriteReference(value, null)) { WriteReference(writer, value); } @@ -382,7 +392,7 @@ namespace Newtonsoft.Json.Serialization string propertyName = entry.Key.ToString(); object value = entry.Value; - if (ShouldWriteReference(value)) + if (ShouldWriteReference(value, null)) { writer.WritePropertyName(propertyName); WriteReference(writer, value); diff --git a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs index 46097be7..ada2960e 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs @@ -43,9 +43,9 @@ namespace Newtonsoft.Json.Serialization public const string TypePropertyName = "$type"; public const string ArrayValuesPropertyName = "$values"; - private static readonly ThreadSafeDictionaryWrapper ConverterTypeCache = new ThreadSafeDictionaryWrapper(GetConverterTypeFromAttribute); + private static readonly ThreadSafeStore ConverterTypeCache = new ThreadSafeStore(GetConverterTypeFromAttribute); #if !SILVERLIGHT && !PocketPC - private static readonly ThreadSafeDictionaryWrapper AssociatedMetadataTypesCache = new ThreadSafeDictionaryWrapper(GetAssociateMetadataTypeFromAttribute); + private static readonly ThreadSafeStore AssociatedMetadataTypesCache = new ThreadSafeStore(GetAssociateMetadataTypeFromAttribute); #endif public static JsonContainerAttribute GetJsonContainerAttribute(Type type) @@ -53,6 +53,11 @@ namespace Newtonsoft.Json.Serialization return CachedAttributeGetter.GetAttribute(type); } + public static JsonObjectAttribute GetJsonObjectAttribute(Type type) + { + return GetJsonContainerAttribute(type) as JsonObjectAttribute; + } + #if !PocketPC public static DataContractAttribute GetDataContractAttribute(Type type) { @@ -62,7 +67,7 @@ namespace Newtonsoft.Json.Serialization public static MemberSerialization GetObjectMemberSerialization(Type objectType) { - JsonObjectAttribute objectAttribute = GetJsonContainerAttribute(objectType) as JsonObjectAttribute; + JsonObjectAttribute objectAttribute = GetJsonObjectAttribute(objectType); if (objectAttribute == null) { diff --git a/Src/Newtonsoft.Json/Serialization/ThreadSafeDictionaryWrapper.cs b/Src/Newtonsoft.Json/Serialization/ThreadSafeDictionaryWrapper.cs deleted file mode 100644 index 651051be..00000000 --- a/Src/Newtonsoft.Json/Serialization/ThreadSafeDictionaryWrapper.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Newtonsoft.Json.Utilities; - -namespace Newtonsoft.Json.Serialization -{ - internal class ThreadSafeDictionaryWrapper - { - private Dictionary _dictionary; - private readonly Func _creator; - - public ThreadSafeDictionaryWrapper(Func creator) - { - ValidationUtils.ArgumentNotNull(creator, "creator"); - - _creator = creator; - } - - private TValue AddValue(TKey key) - { - lock (this) - { - TValue value; - - if (_dictionary == null) - { - _dictionary = new Dictionary(); - value = _creator(key); - _dictionary[key] = value; - } - else - { - // double check locking - if (_dictionary.TryGetValue(key, out value)) - return value; - - Dictionary newDictionary = new Dictionary(_dictionary); - value = _creator(key); - newDictionary[key] = value; - - _dictionary = newDictionary; - } - - return value; - } - } - - public TValue Get(TKey key) - { - if (_dictionary == null) - return AddValue(key); - - TValue value; - if (!_dictionary.TryGetValue(key, out value)) - return AddValue(key); - - return value; - } - } -} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs b/Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs new file mode 100644 index 00000000..9cb6ded5 --- /dev/null +++ b/Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Utilities +{ + internal class ThreadSafeStore + { + private Dictionary _store; + private readonly Func _creator; + + public ThreadSafeStore(Func creator) + { + if (creator == null) + throw new ArgumentNullException("creator"); + + _creator = creator; + } + + public TValue Get(TKey key) + { + if (_store == null) + return AddValue(key); + + TValue value; + if (!_store.TryGetValue(key, out value)) + return AddValue(key); + + return value; + } + + private TValue AddValue(TKey key) + { + lock (this) + { + TValue value; + + if (_store == null) + { + _store = new Dictionary(); + value = _creator(key); + _store[key] = value; + } + else + { + // double check locking + if (_store.TryGetValue(key, out value)) + return value; + + Dictionary newStore = new Dictionary(_store); + value = _creator(key); + newStore[key] = value; + + _store = newStore; + } + + return value; + } + } + } +} \ No newline at end of file