diff options
author | JamesNK <james@newtonking.com> | 2011-01-15 03:44:26 +0300 |
---|---|---|
committer | JamesNK <james@newtonking.com> | 2011-01-15 03:44:26 +0300 |
commit | 35351737167c27eca08de2e0114edb641e33930c (patch) | |
tree | b836f99e136e650313527fd25fb47497630d0e71 /Src/Newtonsoft.Json | |
parent | 5d062cc8f8754a33121198462883ebb3e82de3d0 (diff) |
-Added JsonConstructorAttribute to explicitly define which constructor to use during deserialization
-Added IFormattable to JValue
-Changed JValue ToString to call ToString on the internal value
-Fixed code emit error when deserializing interfaces in certain situations
Diffstat (limited to 'Src/Newtonsoft.Json')
12 files changed, 134 insertions, 26 deletions
diff --git a/Src/Newtonsoft.Json/JsonConstructorAttribute.cs b/Src/Newtonsoft.Json/JsonConstructorAttribute.cs new file mode 100644 index 0000000..cc0d6e6 --- /dev/null +++ b/Src/Newtonsoft.Json/JsonConstructorAttribute.cs @@ -0,0 +1,15 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Newtonsoft.Json
+{
+ /// <summary>
+ /// Instructs the <see cref="JsonSerializer"/> not to serialize the public field or public read/write property value.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Property, AllowMultiple = false)]
+ public sealed class JsonConstructorAttribute : Attribute
+ {
+ }
+}
\ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonValidatingReader.cs b/Src/Newtonsoft.Json/JsonValidatingReader.cs index 5bace6d..cb2435a 100644 --- a/Src/Newtonsoft.Json/JsonValidatingReader.cs +++ b/Src/Newtonsoft.Json/JsonValidatingReader.cs @@ -32,6 +32,7 @@ using Newtonsoft.Json.Schema; using Newtonsoft.Json.Utilities;
using System.Globalization;
using System.Text.RegularExpressions;
+using System.IO;
namespace Newtonsoft.Json
{
@@ -300,8 +301,11 @@ namespace Newtonsoft.Json if (schema.Enum != null)
{
+ StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
+ value.WriteTo(new JsonTextWriter(sw));
+
if (!schema.Enum.ContainsValue(value, new JTokenEqualityComparer()))
- RaiseError("Value {0} is not defined in enum.".FormatWith(CultureInfo.InvariantCulture, value),
+ RaiseError("Value {0} is not defined in enum.".FormatWith(CultureInfo.InvariantCulture, sw.ToString()),
schema);
}
diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index 554acd4..8bbb321 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -40,7 +40,7 @@ namespace Newtonsoft.Json.Linq /// <summary>
/// Represents a value in JSON (string, integer, date, etc).
/// </summary>
- public class JValue : JToken, IEquatable<JValue>
+ public class JValue : JToken, IEquatable<JValue>, IFormattable
{
private JTokenType _valueType;
private object _value;
@@ -406,6 +406,40 @@ namespace Newtonsoft.Json.Linq return _value.GetHashCode();
}
+ /// <summary>
+ /// Returns a <see cref="System.String"/> that represents this instance.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="System.String"/> that represents this instance.
+ /// </returns>
+ public override string ToString()
+ {
+ if (_value == null)
+ return string.Empty;
+
+ return _value.ToString();
+ }
+
+ /// <summary>
+ /// Returns a <see cref="System.String"/> that represents this instance.
+ /// </summary>
+ /// <param name="format">The format.</param>
+ /// <param name="formatProvider">The format provider.</param>
+ /// <returns>
+ /// A <see cref="System.String"/> that represents this instance.
+ /// </returns>
+ public string ToString(string format, IFormatProvider formatProvider)
+ {
+ if (_value == null)
+ return string.Empty;
+
+ IFormattable formattable = _value as IFormattable;
+ if (formattable != null)
+ return formattable.ToString(format, formatProvider);
+ else
+ return _value.ToString();
+ }
+
#if !(NET35 || NET20 || WINDOWS_PHONE)
/// <summary>
/// Returns the <see cref="T:System.Dynamic.DynamicMetaObject"/> responsible for binding operations performed on this object.
@@ -440,6 +474,26 @@ namespace Newtonsoft.Json.Linq result = ConvertUtils.Convert(instance.Value, CultureInfo.InvariantCulture, binder.Type);
return true;
}
+
+ //public override bool TryBinaryOperation(JValue instance, BinaryOperationBinder binder, object arg, out object result)
+ //{
+ // dynamic d1 = arg;
+ // dynamic d2 = instance._value;
+ // //// use built in JValue equals
+ // //if (arg is JValue)
+ // //{
+ // // result = null;
+ // // return false;
+ // //}
+
+ // //if (binder.Operation == ExpressionType.Equal)
+ // //{
+ // // result = Compare(instance.Type, instance.Value, arg);
+ // // return true;
+ // //}
+
+ // //return base.TryBinaryOperation(instance, binder, arg, out result);
+ //}
}
#endif
}
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj index 97c213d..d3a8d9e 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -95,6 +95,7 @@ <Compile Include="Converters\KeyValuePairConverter.cs" />
<Compile Include="Converters\RegexConverter.cs" />
<Compile Include="Converters\StringEnumConverter.cs" />
+ <Compile Include="JsonConstructorAttribute.cs" />
<Compile Include="Linq\ComponentModel\JPropertyDescriptor.cs" />
<Compile Include="Linq\JPath.cs" />
<Compile Include="Linq\JRaw.cs" />
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj index 1e2ba52..3c7fe56 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj @@ -102,6 +102,7 @@ <Compile Include="Converters\RegexConverter.cs" />
<Compile Include="Converters\StringEnumConverter.cs" />
<Compile Include="ConstructorHandling.cs" />
+ <Compile Include="JsonConstructorAttribute.cs" />
<Compile Include="Utilities\DynamicProxy.cs" />
<Compile Include="Linq\JPath.cs" />
<Compile Include="Linq\JRaw.cs" />
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index 3bb79fb..4a8cd1f 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -106,6 +106,7 @@ <Compile Include="DefaultValueHandling.cs" />
<Compile Include="IJsonLineInfo.cs" />
<Compile Include="JsonArrayAttribute.cs" />
+ <Compile Include="JsonConstructorAttribute.cs" />
<Compile Include="JsonContainerAttribute.cs" />
<Compile Include="JsonConvert.cs" />
<Compile Include="JsonConverter.cs" />
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj index 6604d0f..4e194ce 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj @@ -79,6 +79,7 @@ <Compile Include="DefaultValueHandling.cs" />
<Compile Include="IJsonLineInfo.cs" />
<Compile Include="JsonArrayAttribute.cs" />
+ <Compile Include="JsonConstructorAttribute.cs" />
<Compile Include="JsonContainerAttribute.cs" />
<Compile Include="JsonConvert.cs" />
<Compile Include="JsonConverter.cs" />
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index a7ca2cb..2959062 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -101,6 +101,7 @@ <Compile Include="Converters\RegexConverter.cs" />
<Compile Include="Converters\StringEnumConverter.cs" />
<Compile Include="ConstructorHandling.cs" />
+ <Compile Include="JsonConstructorAttribute.cs" />
<Compile Include="Utilities\DynamicProxy.cs" />
<Compile Include="Linq\JPath.cs" />
<Compile Include="Linq\JRaw.cs" />
diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 62c6484..cfbdb51 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -64,7 +64,7 @@ namespace Newtonsoft.Json.Serialization if (!(obj is ResolverContractKey))
return false;
- return Equals((ResolverContractKey) obj);
+ return Equals((ResolverContractKey)obj);
}
public bool Equals(ResolverContractKey other)
@@ -277,17 +277,31 @@ namespace Newtonsoft.Json.Serialization contract.MemberSerialization = JsonTypeReflector.GetObjectMemberSerialization(objectType);
contract.Properties.AddRange(CreateProperties(contract.UnderlyingType, contract.MemberSerialization));
- if (contract.DefaultCreator == null || contract.DefaultCreatorNonPublic)
+ if (objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Any(c => c.IsDefined(typeof(JsonConstructorAttribute), true)))
+ contract.OverrideConstructor = GetAttributeConstructor(objectType);
+ else if (contract.DefaultCreator == null || contract.DefaultCreatorNonPublic)
contract.ParametrizedConstructor = GetParametrizedConstructor(objectType);
return contract;
}
+ private ConstructorInfo GetAttributeConstructor(Type objectType)
+ {
+ IList<ConstructorInfo> markedConstructors = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(c => c.IsDefined(typeof(JsonConstructorAttribute), true)).ToList();
+
+ if (markedConstructors.Count > 1)
+ throw new Exception("Multiple constructors with the JsonConstructorAttribute.");
+ else if (markedConstructors.Count == 1)
+ return markedConstructors[0];
+
+ return null;
+ }
+
private ConstructorInfo GetParametrizedConstructor(Type objectType)
{
- ConstructorInfo[] constructors = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
+ IList<ConstructorInfo> constructors = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
- if (constructors.Length == 1)
+ if (constructors.Count == 1)
return constructors[0];
else
return null;
@@ -309,7 +323,7 @@ namespace Newtonsoft.Json.Serialization }
#if !PocketPC && !NET20
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Runtime.Serialization.DataContractAttribute.#get_IsReference()")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Runtime.Serialization.DataContractAttribute.#get_IsReference()")]
#endif
private void InitializeContract(JsonContract contract)
{
@@ -412,7 +426,7 @@ namespace Newtonsoft.Json.Serialization {
JsonPrimitiveContract contract = new JsonPrimitiveContract(objectType);
InitializeContract(contract);
-
+
return contract;
}
@@ -440,7 +454,7 @@ namespace Newtonsoft.Json.Serialization JsonISerializableContract contract = new JsonISerializableContract(objectType);
InitializeContract(contract);
- ConstructorInfo constructorInfo = objectType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new [] {typeof (SerializationInfo), typeof (StreamingContext)}, null);
+ ConstructorInfo constructorInfo = objectType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(SerializationInfo), typeof(StreamingContext) }, null);
if (constructorInfo != null)
{
MethodCall<object, object> methodCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(constructorInfo);
@@ -574,7 +588,7 @@ namespace Newtonsoft.Json.Serialization if (attributeType == typeof(OnErrorAttribute))
{
if (parameters == null || parameters.Length != 2 || parameters[0].ParameterType != typeof(StreamingContext) || parameters[1].ParameterType != typeof(ErrorContext))
- throw new Exception("Serialization Error Callback '{1}' in type '{0}' must have two parameters of type '{2}' and '{3}'.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType), method, typeof (StreamingContext), typeof(ErrorContext)));
+ throw new Exception("Serialization Error Callback '{1}' in type '{0}' must have two parameters of type '{2}' and '{3}'.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType), method, typeof(StreamingContext), typeof(ErrorContext)));
}
else
{
@@ -646,7 +660,7 @@ namespace Newtonsoft.Json.Serialization JsonProperty property = new JsonProperty();
property.PropertyType = ReflectionUtils.GetMemberUnderlyingType(member);
property.ValueProvider = CreateMemberValueProvider(member);
-
+
// resolve converter for property
// the class type might have a converter but the property converter takes presidence
property.Converter = JsonTypeReflector.GetJsonConverter(member, property.PropertyType);
@@ -689,7 +703,7 @@ namespace Newtonsoft.Json.Serialization (memberSerialization == MemberSerialization.OptIn
&& propertyAttribute == null
#if !PocketPC && !NET20
- && dataMemberAttribute == null
+ && dataMemberAttribute == null
#endif
));
@@ -735,7 +749,7 @@ namespace Newtonsoft.Json.Serialization MethodCall<object, object> shouldSerializeCall =
JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(shouldSerializeMethod);
- return o => (bool) shouldSerializeCall(o);
+ return o => (bool)shouldSerializeCall(o);
}
private void SetIsSpecifiedActions(JsonProperty property, MemberInfo member)
diff --git a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs index e52539f..c3d4e6e 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs @@ -46,6 +46,14 @@ namespace Newtonsoft.Json.Serialization public JsonPropertyCollection Properties { get; private set; }
/// <summary>
+ /// Gets or sets the override constructor used to create the object.
+ /// This is set when a constructor is marked up using the
+ /// JsonConstructor attribute.
+ /// </summary>
+ /// <value>The override constructor.</value>
+ public ConstructorInfo OverrideConstructor { get; set; }
+
+ /// <summary>
/// Gets or sets the parametrized constructor used to create the object.
/// </summary>
/// <value>The parametrized constructor.</value>
diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 2a8e494..5747d9e 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -831,27 +831,35 @@ namespace Newtonsoft.Json.Serialization if (contract.UnderlyingType.IsInterface || contract.UnderlyingType.IsAbstract)
throw new JsonSerializationException("Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantated.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
- if (contract.DefaultCreator != null &&
+ if (contract.OverrideConstructor != null)
+ {
+ if (contract.OverrideConstructor.GetParameters().Length > 0)
+ return CreateObjectFromNonDefaultConstructor(reader, contract, contract.OverrideConstructor, id);
+
+ newObject = contract.OverrideConstructor.Invoke(null);
+ }
+ else if (contract.DefaultCreator != null &&
(!contract.DefaultCreatorNonPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor))
{
newObject = contract.DefaultCreator();
}
-
- if (newObject != null)
+ else if (contract.ParametrizedConstructor != null)
{
- PopulateObject(newObject, reader, contract, id);
- return newObject;
+ return CreateObjectFromNonDefaultConstructor(reader, contract, contract.ParametrizedConstructor, id);
}
- return CreateObjectFromNonDefaultConstructor(reader, contract, id);
+ if (newObject == null)
+ throw new JsonSerializationException("Unable to find a constructor to use for type {0}. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
+
+ PopulateObject(newObject, reader, contract, id);
+ return newObject;
}
- private object CreateObjectFromNonDefaultConstructor(JsonReader reader, JsonObjectContract contract, string id)
+ private object CreateObjectFromNonDefaultConstructor(JsonReader reader, JsonObjectContract contract, ConstructorInfo constructorInfo, string id)
{
- Type objectType = contract.UnderlyingType;
+ ValidationUtils.ArgumentNotNull(constructorInfo, "constructorInfo");
- if (contract.ParametrizedConstructor == null)
- throw new JsonSerializationException("Unable to find a constructor to use for type {0}. A class should either have a default constructor or only one constructor with arguments.".FormatWith(CultureInfo.InvariantCulture, objectType));
+ Type objectType = contract.UnderlyingType;
// create a dictionary to put retrieved values into
IDictionary<JsonProperty, object> propertyValues = contract.Properties.Where(p => !p.Ignored).ToDictionary(kv => kv, kv => (object)null);
@@ -897,7 +905,7 @@ namespace Newtonsoft.Json.Serialization }
} while (!exit && reader.Read());
- IDictionary<ParameterInfo, object> constructorParameters = contract.ParametrizedConstructor.GetParameters().ToDictionary(p => p, p => (object)null);
+ IDictionary<ParameterInfo, object> constructorParameters = constructorInfo.GetParameters().ToDictionary(p => p, p => (object)null);
IDictionary<JsonProperty, object> remainingPropertyValues = new Dictionary<JsonProperty, object>();
foreach (KeyValuePair<JsonProperty, object> propertyValue in propertyValues)
@@ -909,7 +917,7 @@ namespace Newtonsoft.Json.Serialization remainingPropertyValues.Add(propertyValue);
}
- object createdObject = contract.ParametrizedConstructor.Invoke(constructorParameters.Values.ToArray());
+ object createdObject = constructorInfo.Invoke(constructorParameters.Values.ToArray());
if (id != null)
Serializer.ReferenceResolver.AddReference(id, createdObject);
diff --git a/Src/Newtonsoft.Json/Utilities/DynamicReflectionDelegateFactory.cs b/Src/Newtonsoft.Json/Utilities/DynamicReflectionDelegateFactory.cs index e1e6b91..eab9db7 100644 --- a/Src/Newtonsoft.Json/Utilities/DynamicReflectionDelegateFactory.cs +++ b/Src/Newtonsoft.Json/Utilities/DynamicReflectionDelegateFactory.cs @@ -42,7 +42,7 @@ namespace Newtonsoft.Json.Utilities {
DynamicMethod dynamicMethod = !owner.IsInterface
? new DynamicMethod(name, returnType, parameterTypes, owner, true)
- : new DynamicMethod(name, returnType, parameterTypes, (Module)null, true);
+ : new DynamicMethod(name, returnType, parameterTypes, owner.Module, true);
return dynamicMethod;
}
|