-JsonObjectAttribute will now force a collection class to be serialized as an object
-Renamed ThreadSafeDictionaryWrapper to ThreadSafeStore
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user