From 0e22f39e1207294b1e7d965f6c651aca6957f956 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 2 May 2012 23:57:52 +1200 Subject: [PATCH] -Added ItemRequired to JsonObjectAttribute --- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/JsonSerializerTest.cs | 91 ++++++++++++++++++ .../Serialization/TypeNameHandlingTests.cs | 1 - Src/Newtonsoft.Json/JsonObjectAttribute.cs | 16 ++++ Src/Newtonsoft.Json/JsonPropertyAttribute.cs | 25 +++-- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/DefaultContractResolver.cs | 14 +-- .../Serialization/JsonArrayContract.cs | 2 - .../Serialization/JsonContainerContract.cs | 27 ++++++ .../Serialization/JsonDictionaryContract.cs | 3 +- .../Serialization/JsonObjectContract.cs | 8 ++ .../Serialization/JsonProperty.cs | 8 +- .../JsonSerializerInternalReader.cs | 22 +++-- .../JsonSerializerInternalWriter.cs | 94 +++++++++---------- 14 files changed, 236 insertions(+), 79 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index d1599318..e9fa4ea3 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -72,5 +72,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.5.4.14825")] +[assembly: AssemblyFileVersion("4.5.4.14902")] #endif diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index f795d28f..78e2d458 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -6016,6 +6016,83 @@ Parameter name: value", Console.WriteLine(v.Value.GetType()); Console.WriteLine(a.ToString()); } + + [Test] + public void ObjectRequiredDeserializeMissing() + { + string json = "{}"; + IList errors = new List(); + + EventHandler error = (s, e) => + { + errors.Add(e.ErrorContext.Error.Message); + e.ErrorContext.Handled = true; + }; + + var o = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + Error = error + }); + + Assert.IsNotNull(o); + Assert.AreEqual(4, errors.Count); + Assert.AreEqual("Required property 'NonAttributeProperty' not found in JSON. Path '', line 1, position 2.", errors[0]); + Assert.AreEqual("Required property 'UnsetProperty' not found in JSON. Path '', line 1, position 2.", errors[1]); + Assert.AreEqual("Required property 'AllowNullProperty' not found in JSON. Path '', line 1, position 2.", errors[2]); + Assert.AreEqual("Required property 'AlwaysProperty' not found in JSON. Path '', line 1, position 2.", errors[3]); + } + + [Test] + public void ObjectRequiredDeserializeNull() + { + string json = "{'NonAttributeProperty':null,'UnsetProperty':null,'AllowNullProperty':null,'AlwaysProperty':null}"; + IList errors = new List(); + + EventHandler error = (s, e) => + { + errors.Add(e.ErrorContext.Error.Message); + e.ErrorContext.Handled = true; + }; + + var o = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + Error = error + }); + + Assert.IsNotNull(o); + Assert.AreEqual(3, errors.Count); + Assert.AreEqual("Required property 'NonAttributeProperty' expects a value but got null. Path '', line 1, position 97.", errors[0]); + Assert.AreEqual("Required property 'UnsetProperty' expects a value but got null. Path '', line 1, position 97.", errors[1]); + Assert.AreEqual("Required property 'AlwaysProperty' expects a value but got null. Path '', line 1, position 97.", errors[2]); + } + + [Test] + public void ObjectRequiredSerialize() + { + IList errors = new List(); + + EventHandler error = (s, e) => + { + errors.Add(e.ErrorContext.Error.Message); + e.ErrorContext.Handled = true; + }; + + string json = JsonConvert.SerializeObject(new RequiredObject(), new JsonSerializerSettings + { + Error = error, + Formatting = Formatting.Indented + }); + + Assert.AreEqual(@"{ + ""DefaultProperty"": null, + ""AllowNullProperty"": null +}", json); + + Assert.AreEqual(3, errors.Count); + Assert.AreEqual("Cannot write a null value for property 'NonAttributeProperty'. Property requires a value.", errors[0]); + Assert.AreEqual("Cannot write a null value for property 'UnsetProperty'. Property requires a value.", errors[1]); + Assert.AreEqual("Cannot write a null value for property 'AlwaysProperty'. Property requires a value.", errors[2]); + } } public class PersonWithPrivateConstructor @@ -6204,4 +6281,18 @@ Parameter name: value", { } } + + [JsonObject(ItemRequired = Required.Always)] + public class RequiredObject + { + public int? NonAttributeProperty { get; set; } + [JsonProperty] + public int? UnsetProperty { get; set; } + [JsonProperty(Required = Required.Default)] + public int? DefaultProperty { get; set; } + [JsonProperty(Required = Required.AllowNull)] + public int? AllowNullProperty { get; set; } + [JsonProperty(Required = Required.Always)] + public int? AlwaysProperty { get; set; } + } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs index 1fb2cd47..565d2fd2 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs @@ -665,7 +665,6 @@ namespace Newtonsoft.Json.Tests.Serialization }"; Assert.AreEqual(expected, json); - Console.WriteLine(json); StringReader sr = new StringReader(json); diff --git a/Src/Newtonsoft.Json/JsonObjectAttribute.cs b/Src/Newtonsoft.Json/JsonObjectAttribute.cs index 588eefcb..862db6bd 100644 --- a/Src/Newtonsoft.Json/JsonObjectAttribute.cs +++ b/Src/Newtonsoft.Json/JsonObjectAttribute.cs @@ -35,6 +35,10 @@ namespace Newtonsoft.Json { private MemberSerialization _memberSerialization = MemberSerialization.OptOut; + // yuck. can't set nullable properties on an attribute in C# + // have to use this approach to get an unset default state + internal Required? _itemRequired; + /// /// Gets or sets the member serialization. /// @@ -45,6 +49,18 @@ namespace Newtonsoft.Json set { _memberSerialization = value; } } + /// + /// Gets or sets a value that indicates whether the object's properties are required. + /// + /// + /// A value indicating whether the object's properties are required. + /// + public Required ItemRequired + { + get { return _itemRequired ?? default(Required); } + set { _itemRequired = value; } + } + /// /// Initializes a new instance of the class. /// diff --git a/Src/Newtonsoft.Json/JsonPropertyAttribute.cs b/Src/Newtonsoft.Json/JsonPropertyAttribute.cs index 33d4151b..c4a66fe2 100644 --- a/Src/Newtonsoft.Json/JsonPropertyAttribute.cs +++ b/Src/Newtonsoft.Json/JsonPropertyAttribute.cs @@ -42,6 +42,13 @@ namespace Newtonsoft.Json internal TypeNameHandling? _typeNameHandling; internal bool? _isReference; internal int? _order; + internal Required? _required; + + ///// + ///// Gets the collection's items converter. + ///// + ///// The collection's items converter. + //public Type ItemConverterType { get; set; } /// /// Gets or sets the null value handling used when serializing this property. @@ -113,19 +120,23 @@ namespace Newtonsoft.Json set { _order = value; } } - /// - /// Gets or sets the name of the property. - /// - /// The name of the property. - public string PropertyName { get; set; } - /// /// Gets or sets a value indicating whether this property is required. /// /// /// A value indicating whether this property is required. /// - public Required Required { get; set; } + public Required Required + { + get { return _required ?? Required.Default; } + set { _required = value; } + } + + /// + /// Gets or sets the name of the property. + /// + /// The name of the property. + public string PropertyName { get; set; } /// /// Initializes a new instance of the class. diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 95be7bc2..2b9c8f29 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -90,7 +90,7 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.5.4.14825")] +[assembly: AssemblyFileVersion("4.5.4.14902")] #endif [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 8c069bc5..75d482e3 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -358,6 +358,10 @@ namespace Newtonsoft.Json.Serialization contract.MemberSerialization = JsonTypeReflector.GetObjectMemberSerialization(contract.NonNullableUnderlyingType, ignoreSerializableAttribute); contract.Properties.AddRange(CreateProperties(contract.NonNullableUnderlyingType, contract.MemberSerialization)); + JsonObjectAttribute attribute = JsonTypeReflector.GetJsonObjectAttribute(contract.NonNullableUnderlyingType); + if (attribute != null) + contract.ItemRequired = attribute._itemRequired; + // check if a JsonConstructorAttribute has been defined and use that if (contract.NonNullableUnderlyingType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Any(c => c.IsDefined(typeof(JsonConstructorAttribute), true))) { @@ -457,7 +461,7 @@ namespace Newtonsoft.Json.Serialization property.Converter = property.Converter ?? matchingMemberProperty.Converter; property.MemberConverter = property.MemberConverter ?? matchingMemberProperty.MemberConverter; property.DefaultValue = property.DefaultValue ?? matchingMemberProperty.DefaultValue; - property.Required = (property.Required != Required.Default) ? property.Required : matchingMemberProperty.Required; + property._required = property._required ?? matchingMemberProperty._required; property.IsReference = property.IsReference ?? matchingMemberProperty.IsReference; property.NullValueHandling = property.NullValueHandling ?? matchingMemberProperty.NullValueHandling; property.DefaultValueHandling = property.DefaultValueHandling ?? matchingMemberProperty.DefaultValueHandling; @@ -944,20 +948,16 @@ namespace Newtonsoft.Json.Serialization if (propertyAttribute != null) { - property.Required = propertyAttribute.Required; + property._required = propertyAttribute._required; property.Order = propertyAttribute._order; } #if !PocketPC && !NET20 else if (dataMemberAttribute != null) { - property.Required = (dataMemberAttribute.IsRequired) ? Required.AllowNull : Required.Default; + property._required = (dataMemberAttribute.IsRequired) ? Required.AllowNull : Required.Default; property.Order = (dataMemberAttribute.Order != -1) ? (int?) dataMemberAttribute.Order : null; } #endif - else - { - property.Required = Required.Default; - } bool hasJsonIgnoreAttribute = JsonTypeReflector.GetAttribute(attributeProvider) != null #if !(SILVERLIGHT || NETFX_CORE || PORTABLE) diff --git a/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs b/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs index fe76cf48..32299a5b 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs @@ -47,8 +47,6 @@ namespace Newtonsoft.Json.Serialization /// The of the collection items. public Type CollectionItemType { get; private set; } - internal JsonContract CollectionItemContract { get; set; } - private readonly bool _isCollectionItemTypeNullableType; private readonly Type _genericCollectionDefinitionType; private Type _genericWrapperType; diff --git a/Src/Newtonsoft.Json/Serialization/JsonContainerContract.cs b/Src/Newtonsoft.Json/Serialization/JsonContainerContract.cs index b4308837..98783b0b 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonContainerContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonContainerContract.cs @@ -41,6 +41,33 @@ namespace Newtonsoft.Json.Serialization /// public class JsonContainerContract : JsonContract { + private JsonContract _itemContract; + private JsonContract _finalItemContract; + + // will be null for containers that don't have an item type (e.g. IList) or for complex objects + internal JsonContract ItemContract + { + get { return _itemContract; } + set + { + _itemContract = value; + if (_itemContract != null) + { + _finalItemContract = (_itemContract.UnderlyingType.IsSealed()) ? _itemContract : null; + } + else + { + _finalItemContract = null; + } + } + } + + // the final (i.e. can't be inherited from like a sealed class or valuetype) item contract + internal JsonContract FinalItemContract + { + get { return _finalItemContract; } + } + /// /// Gets or sets the default collection items . /// diff --git a/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs b/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs index 2a63e3e3..3fecc844 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs @@ -56,8 +56,7 @@ namespace Newtonsoft.Json.Serialization /// The of the dictionary values. public Type DictionaryValueType { get; private set; } - internal JsonContract DictionaryKeyContract { get; set; } - internal JsonContract DictionaryValueContract { get; set; } + internal JsonContract KeyContract { get; set; } private readonly bool _isDictionaryValueTypeNullableType; private readonly Type _genericCollectionDefinitionType; diff --git a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs index 47a62b5c..86d98d65 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs @@ -39,6 +39,14 @@ namespace Newtonsoft.Json.Serialization /// The member object serialization. public MemberSerialization MemberSerialization { get; set; } + /// + /// Gets or sets a value that indicates whether the object's properties are required. + /// + /// + /// A value indicating whether the object's properties are required. + /// + public Required? ItemRequired { get; set; } + /// /// Gets the object's properties. /// diff --git a/Src/Newtonsoft.Json/Serialization/JsonProperty.cs b/Src/Newtonsoft.Json/Serialization/JsonProperty.cs index 5d67a8f2..3faf1de6 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonProperty.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonProperty.cs @@ -111,11 +111,17 @@ namespace Newtonsoft.Json.Serialization /// The default value. public object DefaultValue { get; set; } + internal Required? _required; + /// /// Gets a value indicating whether this is required. /// /// A value indicating whether this is required. - public Required Required { get; set; } + public Required Required + { + get { return _required ?? Required.Default; } + set { _required = value; } + } /// /// Gets a value indicating whether this property preserves object references. diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index de65037a..c8e02732 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -703,27 +703,27 @@ To force JSON arrays to deserialize add the JsonArrayAttribute to the type.".For object keyValue = reader.Value; try { - if (contract.DictionaryKeyContract == null) - contract.DictionaryKeyContract = GetContractSafe(contract.DictionaryKeyType); + if (contract.KeyContract == null) + contract.KeyContract = GetContractSafe(contract.DictionaryKeyType); try { - keyValue = EnsureType(reader, keyValue, CultureInfo.InvariantCulture, contract.DictionaryKeyContract, contract.DictionaryKeyType); + keyValue = EnsureType(reader, keyValue, CultureInfo.InvariantCulture, contract.KeyContract, contract.DictionaryKeyType); } catch (Exception ex) { throw JsonSerializationException.Create(reader, "Could not convert string '{0}' to dictionary key type '{1}'. Create a TypeConverter to convert from the string to the key type object.".FormatWith(CultureInfo.InvariantCulture, reader.Value, contract.DictionaryKeyType), ex); } - if (contract.DictionaryValueContract == null) - contract.DictionaryValueContract = GetContractSafe(contract.DictionaryValueType); + if (contract.ItemContract == null) + contract.ItemContract = GetContractSafe(contract.DictionaryValueType); - JsonConverter dictionaryValueConverter = contract.ItemConverter ?? GetConverter(contract.DictionaryValueContract, null, contract); + JsonConverter dictionaryValueConverter = contract.ItemConverter ?? GetConverter(contract.ItemContract, null, contract); - if (!ReadForType(reader, contract.DictionaryValueContract, dictionaryValueConverter != null, false)) + if (!ReadForType(reader, contract.ItemContract, dictionaryValueConverter != null, false)) throw JsonSerializationException.Create(reader, "Unexpected end when deserializing object."); - dictionary[keyValue] = CreateValueNonProperty(reader, contract.DictionaryValueType, contract.DictionaryValueContract, dictionaryValueConverter, contract); + dictionary[keyValue] = CreateValueNonProperty(reader, contract.DictionaryValueType, contract.ItemContract, dictionaryValueConverter, contract); } catch (Exception ex) { @@ -1244,10 +1244,12 @@ To fix this error either change the environment to be fully trusted, change the { try { + Required resolvedRequired = property._required ?? contract.ItemRequired ?? Required.Default; + switch (presence) { case PropertyPresence.None: - if (property.Required == Required.AllowNull || property.Required == Required.Always) + if (resolvedRequired == Required.AllowNull || resolvedRequired == Required.Always) throw JsonSerializationException.Create(reader, "Required property '{0}' not found in JSON.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName)); if (property.PropertyContract == null) @@ -1258,7 +1260,7 @@ To fix this error either change the environment to be fully trusted, change the property.ValueProvider.SetValue(newObject, EnsureType(reader, property.DefaultValue, CultureInfo.InvariantCulture, property.PropertyContract, property.PropertyType)); break; case PropertyPresence.Null: - if (property.Required == Required.Always) + if (resolvedRequired == Required.Always) throw JsonSerializationException.Create(reader, "Required property '{0}' expects a value but got null.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName)); break; } diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 433a7172..291ccdaa 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -58,7 +58,7 @@ namespace Newtonsoft.Json.Serialization if (jsonWriter == null) throw new ArgumentNullException("jsonWriter"); - SerializeValue(jsonWriter, value, GetContractSafe(value), null, null, null); + SerializeValue(jsonWriter, value, GetContractSafe(value), null, null); } private JsonSerializerProxy GetInternalSerializer() @@ -77,11 +77,11 @@ namespace Newtonsoft.Json.Serialization return Serializer.ContractResolver.ResolveContract(value.GetType()); } - private void SerializePrimitive(JsonWriter writer, object value, JsonPrimitiveContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonContract collectionValueContract) + private void SerializePrimitive(JsonWriter writer, object value, JsonPrimitiveContract contract, JsonProperty member, JsonContainerContract collectionContract) { if (contract.UnderlyingType == typeof (byte[])) { - bool includeTypeDetails = ShouldWriteType(TypeNameHandling.Objects, contract, member, collectionContract, collectionValueContract); + bool includeTypeDetails = ShouldWriteType(TypeNameHandling.Objects, contract, member, collectionContract); if (includeTypeDetails) { writer.WriteStartObject(); @@ -96,7 +96,7 @@ namespace Newtonsoft.Json.Serialization writer.WriteValue(value); } - private void SerializeValue(JsonWriter writer, object value, JsonContract valueContract, JsonProperty member, JsonContainerContract collectionContract, JsonContract collectionValueContract) + private void SerializeValue(JsonWriter writer, object value, JsonContract valueContract, JsonProperty member, JsonContainerContract collectionContract) { if (value == null) { @@ -112,37 +112,37 @@ namespace Newtonsoft.Json.Serialization || ((converter = valueContract.InternalConverter) != null)) && converter.CanWrite) { - SerializeConvertable(writer, converter, value, valueContract, collectionContract, collectionValueContract); + SerializeConvertable(writer, converter, value, valueContract, collectionContract); return; } switch (valueContract.ContractType) { case JsonContractType.Object: - SerializeObject(writer, value, (JsonObjectContract) valueContract, member, collectionContract, collectionValueContract); + SerializeObject(writer, value, (JsonObjectContract) valueContract, member, collectionContract); break; case JsonContractType.Array: JsonArrayContract arrayContract = (JsonArrayContract) valueContract; - SerializeList(writer, arrayContract.CreateWrapper(value), arrayContract, member, collectionContract, collectionValueContract); + SerializeList(writer, arrayContract.CreateWrapper(value), arrayContract, member, collectionContract); break; case JsonContractType.Primitive: - SerializePrimitive(writer, value, (JsonPrimitiveContract)valueContract, member, collectionContract, collectionValueContract); + SerializePrimitive(writer, value, (JsonPrimitiveContract)valueContract, member, collectionContract); break; case JsonContractType.String: SerializeString(writer, value, (JsonStringContract) valueContract); break; case JsonContractType.Dictionary: JsonDictionaryContract dictionaryContract = (JsonDictionaryContract) valueContract; - SerializeDictionary(writer, dictionaryContract.CreateWrapper(value), dictionaryContract, member, collectionContract, collectionValueContract); + SerializeDictionary(writer, dictionaryContract.CreateWrapper(value), dictionaryContract, member, collectionContract); break; #if !(NET35 || NET20 || WINDOWS_PHONE || PORTABLE) case JsonContractType.Dynamic: - SerializeDynamic(writer, (IDynamicMetaObjectProvider)value, (JsonDynamicContract)valueContract, member, collectionContract, collectionValueContract); + SerializeDynamic(writer, (IDynamicMetaObjectProvider)value, (JsonDynamicContract)valueContract, member, collectionContract); break; #endif #if !(SILVERLIGHT || NETFX_CORE || PORTABLE) case JsonContractType.Serializable: - SerializeISerializable(writer, (ISerializable)value, (JsonISerializableContract)valueContract, member, collectionContract, collectionValueContract); + SerializeISerializable(writer, (ISerializable)value, (JsonISerializableContract)valueContract, member, collectionContract); break; #endif case JsonContractType.Linq: @@ -191,7 +191,7 @@ namespace Newtonsoft.Json.Serialization return Serializer.ReferenceResolver.IsReferenced(this, value); } - private void WriteMemberInfoProperty(JsonWriter writer, object memberValue, JsonProperty property, JsonContract contract, JsonContainerContract collectionContract) + private void WriteMemberInfoProperty(JsonWriter writer, object memberValue, JsonProperty property, JsonContract contract, JsonObjectContract collectionContract) { string propertyName = property.PropertyName; @@ -205,11 +205,15 @@ namespace Newtonsoft.Json.Serialization if (!CheckForCircularReference(memberValue, property, contract, collectionContract)) return; - if (memberValue == null && property.Required == Required.Always) - throw new JsonSerializationException("Cannot write a null value for property '{0}'. Property requires a value.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName)); + if (memberValue == null) + { + Required resolvedRequired = property._required ?? collectionContract.ItemRequired ?? Required.Default; + if (resolvedRequired == Required.Always) + throw new JsonSerializationException("Cannot write a null value for property '{0}'. Property requires a value.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName)); + } writer.WritePropertyName(propertyName); - SerializeValue(writer, memberValue, contract, property, collectionContract, null); + SerializeValue(writer, memberValue, contract, property, collectionContract); } private bool ShouldWriteProperty(object memberValue, JsonProperty property) @@ -317,13 +321,13 @@ namespace Newtonsoft.Json.Serialization contract.InvokeOnSerialized(value, Serializer.Context); } - private void SerializeObject(JsonWriter writer, object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonContract collectionValueContract) + private void SerializeObject(JsonWriter writer, object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract) { contract.InvokeOnSerializing(value, Serializer.Context); _serializeStack.Add(value); - WriteObjectStart(writer, value, contract, member, collectionContract, collectionValueContract); + WriteObjectStart(writer, value, contract, member, collectionContract); int initialDepth = writer.Top; @@ -361,7 +365,7 @@ namespace Newtonsoft.Json.Serialization contract.InvokeOnSerialized(value, Serializer.Context); } - private void WriteObjectStart(JsonWriter writer, object value, JsonContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonContract collectionValueContract) + private void WriteObjectStart(JsonWriter writer, object value, JsonContract contract, JsonProperty member, JsonContainerContract collectionContract) { writer.WriteStartObject(); @@ -371,7 +375,7 @@ namespace Newtonsoft.Json.Serialization writer.WritePropertyName(JsonTypeReflector.IdPropertyName); writer.WriteValue(Serializer.ReferenceResolver.GetReference(this, value)); } - if (ShouldWriteType(TypeNameHandling.Objects, contract, member, collectionContract, collectionValueContract)) + if (ShouldWriteType(TypeNameHandling.Objects, contract, member, collectionContract)) { WriteTypeProperty(writer, contract.UnderlyingType); } @@ -398,7 +402,7 @@ namespace Newtonsoft.Json.Serialization return ((value & flag) == flag); } - private void SerializeConvertable(JsonWriter writer, JsonConverter converter, object value, JsonContract contract, JsonContainerContract collectionContract, JsonContract collectionValueContract) + private void SerializeConvertable(JsonWriter writer, JsonConverter converter, object value, JsonContract contract, JsonContainerContract collectionContract) { if (ShouldWriteReference(value, null, contract, collectionContract)) { @@ -417,18 +421,16 @@ namespace Newtonsoft.Json.Serialization } } - private void SerializeList(JsonWriter writer, IWrappedCollection values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonContract collectionValueContract) + private void SerializeList(JsonWriter writer, IWrappedCollection values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract) { contract.InvokeOnSerializing(values.UnderlyingCollection, Serializer.Context); _serializeStack.Add(values.UnderlyingCollection); - bool hasWrittenMetadataObject = WriteStartArray(writer, values, contract, member, collectionContract, collectionValueContract); + bool hasWrittenMetadataObject = WriteStartArray(writer, values, contract, member, collectionContract); writer.WriteStartArray(); - JsonContract collectionItemValueContract = (contract.CollectionItemContract.UnderlyingType.IsSealed()) ? contract.CollectionItemContract : null; - int initialDepth = writer.Top; int index = 0; @@ -437,7 +439,7 @@ namespace Newtonsoft.Json.Serialization { try { - JsonContract valueContract = collectionItemValueContract ?? GetContractSafe(value); + JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(value); if (ShouldWriteReference(value, null, valueContract, contract)) { @@ -447,7 +449,7 @@ namespace Newtonsoft.Json.Serialization { if (CheckForCircularReference(value, null, valueContract, contract)) { - SerializeValue(writer, value, valueContract, null, contract, contract.CollectionItemContract); + SerializeValue(writer, value, valueContract, null, contract); } } } @@ -474,10 +476,10 @@ namespace Newtonsoft.Json.Serialization contract.InvokeOnSerialized(values.UnderlyingCollection, Serializer.Context); } - private bool WriteStartArray(JsonWriter writer, IWrappedCollection values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonContract collectionValueContract) + private bool WriteStartArray(JsonWriter writer, IWrappedCollection values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract) { bool isReference = ResolveIsReference(contract, member, collectionContract) ?? HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Arrays); - bool includeTypeDetails = ShouldWriteType(TypeNameHandling.Arrays, contract, member, collectionContract, collectionValueContract); + bool includeTypeDetails = ShouldWriteType(TypeNameHandling.Arrays, contract, member, collectionContract); bool writeMetadataObject = isReference || includeTypeDetails; if (writeMetadataObject) @@ -496,8 +498,8 @@ namespace Newtonsoft.Json.Serialization writer.WritePropertyName(JsonTypeReflector.ArrayValuesPropertyName); } - if (contract.CollectionItemContract == null) - contract.CollectionItemContract = Serializer.ContractResolver.ResolveContract(contract.CollectionItemType ?? typeof (object)); + if (contract.ItemContract == null) + contract.ItemContract = Serializer.ContractResolver.ResolveContract(contract.CollectionItemType ?? typeof (object)); return writeMetadataObject; } @@ -507,7 +509,7 @@ namespace Newtonsoft.Json.Serialization [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Security.SecuritySafeCriticalAttribute")] [SecuritySafeCritical] #endif - private void SerializeISerializable(JsonWriter writer, ISerializable value, JsonISerializableContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonContract collectionValueContract) + private void SerializeISerializable(JsonWriter writer, ISerializable value, JsonISerializableContract contract, JsonProperty member, JsonContainerContract collectionContract) { if (!JsonTypeReflector.FullyTrusted) { @@ -518,7 +520,7 @@ To fix this error either change the environment to be fully trusted, change the contract.InvokeOnSerializing(value, Serializer.Context); _serializeStack.Add(value); - WriteObjectStart(writer, value, contract, member, collectionContract, collectionValueContract); + WriteObjectStart(writer, value, contract, member, collectionContract); SerializationInfo serializationInfo = new SerializationInfo(contract.UnderlyingType, new FormatterConverter()); value.GetObjectData(serializationInfo, Serializer.Context); @@ -526,7 +528,7 @@ To fix this error either change the environment to be fully trusted, change the foreach (SerializationEntry serializationEntry in serializationInfo) { writer.WritePropertyName(serializationEntry.Name); - SerializeValue(writer, serializationEntry.Value, GetContractSafe(serializationEntry.Value), null, null, null); + SerializeValue(writer, serializationEntry.Value, GetContractSafe(serializationEntry.Value), null, null); } writer.WriteEndObject(); @@ -537,12 +539,12 @@ To fix this error either change the environment to be fully trusted, change the #endif #if !(NET35 || NET20 || WINDOWS_PHONE || PORTABLE) - private void SerializeDynamic(JsonWriter writer, IDynamicMetaObjectProvider value, JsonDynamicContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonContract collectionValueContract) + private void SerializeDynamic(JsonWriter writer, IDynamicMetaObjectProvider value, JsonDynamicContract contract, JsonProperty member, JsonContainerContract collectionContract) { contract.InvokeOnSerializing(value, Serializer.Context); _serializeStack.Add(value); - WriteObjectStart(writer, value, contract, member, collectionContract, collectionValueContract); + WriteObjectStart(writer, value, contract, member, collectionContract); foreach (string memberName in value.GetDynamicMemberNames()) { @@ -554,7 +556,7 @@ To fix this error either change the environment to be fully trusted, change the : memberName; writer.WritePropertyName(resolvedPropertyName); - SerializeValue(writer, memberValue, GetContractSafe(memberValue), null, null, null); + SerializeValue(writer, memberValue, GetContractSafe(memberValue), null, null); } } @@ -565,7 +567,7 @@ To fix this error either change the environment to be fully trusted, change the } #endif - private bool ShouldWriteType(TypeNameHandling typeNameHandlingFlag, JsonContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonContract collectionValueContract) + private bool ShouldWriteType(TypeNameHandling typeNameHandlingFlag, JsonContract contract, JsonProperty member, JsonContainerContract collectionContract) { TypeNameHandling resolvedTypeNameHandling = ((member != null) ? member.TypeNameHandling : null) ?? ((collectionContract != null) ? collectionContract.ItemTypeNameHandling : null) @@ -586,27 +588,25 @@ To fix this error either change the environment to be fully trusted, change the return true; } } - else if (collectionValueContract != null) + else if (collectionContract != null && collectionContract.ItemContract != null) { - if (Serializer.TypeNameHandling == TypeNameHandling.Auto && contract.UnderlyingType != collectionValueContract.UnderlyingType) + if (Serializer.TypeNameHandling == TypeNameHandling.Auto && contract.UnderlyingType != collectionContract.ItemContract.CreatedType) return true; } return false; } - private void SerializeDictionary(JsonWriter writer, IWrappedDictionary values, JsonDictionaryContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonContract collectionValueContract) + private void SerializeDictionary(JsonWriter writer, IWrappedDictionary values, JsonDictionaryContract contract, JsonProperty member, JsonContainerContract collectionContract) { contract.InvokeOnSerializing(values.UnderlyingDictionary, Serializer.Context); _serializeStack.Add(values.UnderlyingDictionary); - WriteObjectStart(writer, values.UnderlyingDictionary, contract, member, collectionContract, collectionValueContract); + WriteObjectStart(writer, values.UnderlyingDictionary, contract, member, collectionContract); - if (contract.DictionaryValueContract == null) - contract.DictionaryValueContract = Serializer.ContractResolver.ResolveContract(contract.DictionaryValueType ?? typeof(object)); - - JsonContract dictionaryValueContract = (contract.DictionaryValueContract.UnderlyingType.IsSealed()) ? contract.DictionaryValueContract : null; + if (contract.ItemContract == null) + contract.ItemContract = Serializer.ContractResolver.ResolveContract(contract.DictionaryValueType ?? typeof(object)); int initialDepth = writer.Top; @@ -624,7 +624,7 @@ To fix this error either change the environment to be fully trusted, change the try { object value = entry.Value; - JsonContract valueContract = dictionaryValueContract ?? GetContractSafe(value); + JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(value); if (ShouldWriteReference(value, null, valueContract, contract)) { @@ -638,7 +638,7 @@ To fix this error either change the environment to be fully trusted, change the writer.WritePropertyName(propertyName); - SerializeValue(writer, value, valueContract, null, contract, dictionaryValueContract); + SerializeValue(writer, value, valueContract, null, contract); } } catch (Exception ex)