-JsonObjectAttribute will now force a collection class to be serialized as an object

-Renamed ThreadSafeDictionaryWrapper to ThreadSafeStore
This commit is contained in:
JamesNK
2009-06-18 12:23:49 +00:00
parent 63160b04ff
commit 67654ec106
12 changed files with 201 additions and 88 deletions
@@ -2623,5 +2623,79 @@ keyword such as type of business.""
Assert.AreEqual(@"{""bar"":{""baz"":13}}", json);
}
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class Content : IEnumerable<Content>
{
[JsonProperty]
public List<Content> Children;
[JsonProperty]
public string Text;
public IEnumerator GetEnumerator()
{
return Children.GetEnumerator();
}
IEnumerator<Content> IEnumerable<Content>.GetEnumerator()
{
return Children.GetEnumerator();
}
}
[Test]
public void SerializeEnumerableAsObject()
{
Content content = new Content
{
Text = "Blah, blah, blah",
Children = new List<Content>
{
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<Content>(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);
}
}
}
+28 -1
View File
@@ -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(@"<div class='s1'>
<div class='avatar'>
<a href='asdf'>asdf</a><br />
<strong>0</strong>
</div>
<div class='sl'>
<p>
444444444
</p>
</div>
<div class='clear'>
</div>
</div>");
Assert.AreEqual(@"{
""rc"": 200,
""m"": """",
""o"": ""<div class='s1'>\r\n <div class='avatar'> \r\n <a href='asdf'>asdf</a><br />\r\n <strong>0</strong>\r\n </div>\r\n <div class='sl'>\r\n <p>\r\n 444444444\r\n </p>\r\n </div>\r\n <div class='clear'>\r\n </div> \r\n</div>""
}", o.ToString());
}
}
}
@@ -135,7 +135,6 @@
<Compile Include="Serialization\JsonSerializerReader.cs" />
<Compile Include="Serialization\JsonSerializerWriter.cs" />
<Compile Include="Serialization\JsonTypeReflector.cs" />
<Compile Include="Serialization\ThreadSafeDictionaryWrapper.cs" />
<Compile Include="TypeNameHandling.cs" />
<Compile Include="Utilities\BidirectionalDictionary.cs" />
<Compile Include="Utilities\ConvertUtils.cs" />
@@ -156,6 +155,7 @@
<Compile Include="Utilities\ReflectionUtils.cs" />
<Compile Include="Utilities\StringBuffer.cs" />
<Compile Include="Utilities\StringUtils.cs" />
<Compile Include="Utilities\ThreadSafeStore.cs" />
<Compile Include="Utilities\ValidationUtils.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CompactFramework.CSharp.targets" />
@@ -113,7 +113,6 @@
<Compile Include="Serialization\JsonSerializerProxy.cs" />
<Compile Include="Serialization\JsonSerializerReader.cs" />
<Compile Include="Serialization\JsonSerializerWriter.cs" />
<Compile Include="Serialization\ThreadSafeDictionaryWrapper.cs" />
<Compile Include="TypeNameHandling.cs" />
<Compile Include="Utilities\BidirectionalDictionary.cs" />
<Compile Include="Utilities\CollectionUtils.cs" />
@@ -131,6 +130,7 @@
<Compile Include="Utilities\ReflectionUtils.cs" />
<Compile Include="Utilities\StringBuffer.cs" />
<Compile Include="Utilities\StringUtils.cs" />
<Compile Include="Utilities\ThreadSafeStore.cs" />
<Compile Include="Utilities\ValidationUtils.cs" />
<Compile Include="Schema\Extensions.cs" />
<Compile Include="Schema\JsonSchemaException.cs" />
+1 -1
View File
@@ -161,7 +161,7 @@
<Compile Include="Serialization\JsonSerializerWriter.cs" />
<Compile Include="Serialization\JsonTypeReflector.cs" />
<Compile Include="Serialization\CachedAttributeGetter.cs" />
<Compile Include="Serialization\ThreadSafeDictionaryWrapper.cs" />
<Compile Include="Utilities\ThreadSafeStore.cs" />
<Compile Include="TypeNameHandling.cs" />
<Compile Include="Utilities\BidirectionalDictionary.cs" />
<Compile Include="Utilities\ConvertUtils.cs" />
@@ -34,7 +34,7 @@ namespace Newtonsoft.Json.Serialization
{
internal static class CachedAttributeGetter<T> where T : Attribute
{
private static readonly ThreadSafeDictionaryWrapper<ICustomAttributeProvider, T> TypeAttributeCache = new ThreadSafeDictionaryWrapper<ICustomAttributeProvider, T>(JsonTypeReflector.GetAttribute<T>);
private static readonly ThreadSafeStore<ICustomAttributeProvider, T> TypeAttributeCache = new ThreadSafeStore<ICustomAttributeProvider, T>(JsonTypeReflector.GetAttribute<T>);
public static T GetAttribute(ICustomAttributeProvider type)
{
@@ -39,7 +39,7 @@ namespace Newtonsoft.Json.Serialization
{
internal static readonly IMappingResolver Instance = new DefaultMappingResolver();
private readonly ThreadSafeDictionaryWrapper<Type, JsonMemberMappingCollection> _typeMemberMappingsCache;
private readonly ThreadSafeStore<Type, JsonMemberMappingCollection> _typeMemberMappingsCache;
/// <summary>
/// Gets or sets the default members search flags.
@@ -54,7 +54,7 @@ namespace Newtonsoft.Json.Serialization
{
DefaultMembersSearchFlags = BindingFlags.Public | BindingFlags.Instance;
_typeMemberMappingsCache = new ThreadSafeDictionaryWrapper<Type, JsonMemberMappingCollection>(CreateMemberMappings);
_typeMemberMappingsCache = new ThreadSafeStore<Type, JsonMemberMappingCollection>(CreateMemberMappings);
}
/// <summary>
@@ -132,7 +132,7 @@ namespace Newtonsoft.Json.Serialization
/// <returns>A created <see cref="JsonMemberMapping"/> for the given <see cref="MemberInfo"/>.</returns>
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);
}
@@ -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
/// <param name="nullValueHandling">Mapping null value handling.</param>
/// <param name="defaultValueHandling">Mapping default value handling.</param>
/// <param name="referenceLoopHandling">Mapping reference loop handling.</param>
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
/// <value>
/// <c>true</c> if this instance is reference; otherwise, <c>false</c>.
/// </value>
public bool IsReference
public bool? IsReference
{
get { return _isReference; }
}
@@ -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);
@@ -43,9 +43,9 @@ namespace Newtonsoft.Json.Serialization
public const string TypePropertyName = "$type";
public const string ArrayValuesPropertyName = "$values";
private static readonly ThreadSafeDictionaryWrapper<ICustomAttributeProvider, Type> ConverterTypeCache = new ThreadSafeDictionaryWrapper<ICustomAttributeProvider, Type>(GetConverterTypeFromAttribute);
private static readonly ThreadSafeStore<ICustomAttributeProvider, Type> ConverterTypeCache = new ThreadSafeStore<ICustomAttributeProvider, Type>(GetConverterTypeFromAttribute);
#if !SILVERLIGHT && !PocketPC
private static readonly ThreadSafeDictionaryWrapper<Type, Type> AssociatedMetadataTypesCache = new ThreadSafeDictionaryWrapper<Type, Type>(GetAssociateMetadataTypeFromAttribute);
private static readonly ThreadSafeStore<Type, Type> AssociatedMetadataTypesCache = new ThreadSafeStore<Type, Type>(GetAssociateMetadataTypeFromAttribute);
#endif
public static JsonContainerAttribute GetJsonContainerAttribute(Type type)
@@ -53,6 +53,11 @@ namespace Newtonsoft.Json.Serialization
return CachedAttributeGetter<JsonContainerAttribute>.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)
{
@@ -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<TKey, TValue>
{
private Dictionary<TKey, TValue> _dictionary;
private readonly Func<TKey, TValue> _creator;
public ThreadSafeDictionaryWrapper(Func<TKey, TValue> creator)
{
ValidationUtils.ArgumentNotNull(creator, "creator");
_creator = creator;
}
private TValue AddValue(TKey key)
{
lock (this)
{
TValue value;
if (_dictionary == null)
{
_dictionary = new Dictionary<TKey, TValue>();
value = _creator(key);
_dictionary[key] = value;
}
else
{
// double check locking
if (_dictionary.TryGetValue(key, out value))
return value;
Dictionary<TKey, TValue> newDictionary = new Dictionary<TKey, TValue>(_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;
}
}
}
@@ -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<TKey, TValue>
{
private Dictionary<TKey, TValue> _store;
private readonly Func<TKey, TValue> _creator;
public ThreadSafeStore(Func<TKey, TValue> 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<TKey, TValue>();
value = _creator(key);
_store[key] = value;
}
else
{
// double check locking
if (_store.TryGetValue(key, out value))
return value;
Dictionary<TKey, TValue> newStore = new Dictionary<TKey, TValue>(_store);
value = _creator(key);
newStore[key] = value;
_store = newStore;
}
return value;
}
}
}
}