diff options
author | JamesNK <james@newtonking.com> | 2010-11-25 11:17:22 +0300 |
---|---|---|
committer | JamesNK <james@newtonking.com> | 2010-11-25 11:17:22 +0300 |
commit | 2eb0d3bf48f1fc675de46ee94911a71ccd62c66f (patch) | |
tree | 9adaddadf242ff44b0695283207e6c433fc5f672 /Src | |
parent | efc2310a3dbcfbd6820dd57ad62ffa473258f5bc (diff) |
-Added XmlSerializer style Specified property support
Diffstat (limited to 'Src')
7 files changed, 192 insertions, 10 deletions
diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 82b5f58..c3283e0 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -3378,6 +3378,138 @@ keyword such as type of business."" Console.WriteLine(json);
}
+ public class SpecifiedTestClass
+ {
+ private bool _nameSpecified;
+
+ public string Name { get; set; }
+ public int Age { get; set; }
+ public int Weight { get; set; }
+
+ // dummy. should never be used because it isn't of type bool
+ [JsonIgnore]
+ public long AgeSpecified { get; set; }
+
+ [JsonIgnore]
+ public bool NameSpecified
+ {
+ get { return _nameSpecified; }
+ set { _nameSpecified = value; }
+ }
+
+ [JsonIgnore]
+ public bool WeightSpecified;
+ }
+
+ [Test]
+ public void SpecifiedTest()
+ {
+ SpecifiedTestClass c = new SpecifiedTestClass();
+ c.Name = "James";
+ c.Age = 27;
+ c.NameSpecified = false;
+
+ string json = JsonConvert.SerializeObject(c, Formatting.Indented);
+
+ Assert.AreEqual(@"{
+ ""Age"": 27
+}", json);
+
+ SpecifiedTestClass deserialized = JsonConvert.DeserializeObject<SpecifiedTestClass>(json);
+ Assert.IsNull(deserialized.Name);
+ Assert.IsFalse(deserialized.NameSpecified);
+ Assert.IsFalse(deserialized.WeightSpecified);
+ Assert.AreEqual(27, deserialized.Age);
+
+ c.NameSpecified = true;
+ c.WeightSpecified = true;
+ json = JsonConvert.SerializeObject(c, Formatting.Indented);
+
+ Assert.AreEqual(@"{
+ ""Name"": ""James"",
+ ""Age"": 27,
+ ""Weight"": 0
+}", json);
+
+ deserialized = JsonConvert.DeserializeObject<SpecifiedTestClass>(json);
+ Assert.AreEqual("James", deserialized.Name);
+ Assert.IsTrue(deserialized.NameSpecified);
+ Assert.IsTrue(deserialized.WeightSpecified);
+ Assert.AreEqual(27, deserialized.Age);
+
+// XmlSerializer s = new XmlSerializer(typeof(OptionalOrder));
+
+// StringWriter sw = new StringWriter();
+// s.Serialize(sw, new OptionalOrder() { FirstOrder = "First", FirstOrderSpecified = true});
+
+// Console.WriteLine(sw.ToString());
+
+// string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
+//<OptionalOrder xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
+// <FirstOrder>First</FirstOrder>
+//</OptionalOrder>";
+
+// OptionalOrder o = (OptionalOrder)s.Deserialize(new StringReader(xml));
+// Console.WriteLine(o.FirstOrder);
+// Console.WriteLine(o.FirstOrderSpecified);
+ }
+
+ //public class OptionalOrder
+ //{
+ // // This field shouldn't be serialized
+ // // if it is uninitialized.
+ // public string FirstOrder;
+ // // Use the XmlIgnoreAttribute to ignore the
+ // // special field named "FirstOrderSpecified".
+ // [System.Xml.Serialization.XmlIgnoreAttribute]
+ // public bool FirstOrderSpecified;
+ //}
+
+ public class FamilyDetails
+ {
+ public string Name { get; set; }
+ public int NumberOfChildren { get; set; }
+
+ [JsonIgnore]
+ public bool NumberOfChildrenSpecified { get; set; }
+ }
+
+ [Test]
+ public void SpecifiedExample()
+ {
+ FamilyDetails joe = new FamilyDetails();
+ joe.Name = "Joe Family Details";
+ joe.NumberOfChildren = 4;
+ joe.NumberOfChildrenSpecified = true;
+
+ FamilyDetails martha = new FamilyDetails();
+ martha.Name = "Martha Family Details";
+ martha.NumberOfChildren = 3;
+ martha.NumberOfChildrenSpecified = false;
+
+ string json = JsonConvert.SerializeObject(new[] { joe, martha }, Formatting.Indented);
+ //[
+ // {
+ // "Name": "Joe Family Details",
+ // "NumberOfChildren": 4
+ // },
+ // {
+ // "Name": "Martha Family Details"
+ // }
+ //]
+ Console.WriteLine(json);
+
+ string mikeString = "{\"Name\": \"Mike Person\"}";
+ FamilyDetails mike = JsonConvert.DeserializeObject<FamilyDetails>(mikeString);
+
+ Console.WriteLine("mikeString specifies number of children: {0}", mike.NumberOfChildrenSpecified);
+
+ string mikeFullDisclosureString = "{\"Name\": \"Mike Person\", \"NumberOfChildren\": \"0\"}";
+ mike = JsonConvert.DeserializeObject<FamilyDetails>(mikeFullDisclosureString);
+
+ Console.WriteLine("mikeString specifies number of children: {0}", mike.NumberOfChildrenSpecified);
+ }
+
public class DictionaryKey
{
public string Value { get; set; }
diff --git a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs index 1e6b60b..f906ebb 100644 --- a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs +++ b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs @@ -361,7 +361,8 @@ namespace Newtonsoft.Json.Schema {
bool optional = property.NullValueHandling == NullValueHandling.Ignore ||
property.DefaultValueHandling == DefaultValueHandling.Ignore ||
- property.ShouldSerialize != null;
+ property.ShouldSerialize != null ||
+ property.GetIsSpecified != null;
JsonSchema propertySchema = GenerateInternal(property.PropertyType, property.Required, optional);
diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index edc91df..2632593 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -35,6 +35,7 @@ using System.Linq; using System.Reflection;
using System.Runtime.Serialization;
using System.Security.Permissions;
+using System.Xml.Serialization;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Utilities;
using Newtonsoft.Json.Linq;
@@ -717,6 +718,8 @@ namespace Newtonsoft.Json.Serialization property.ShouldSerialize = CreateShouldSerializeTest(member);
+ SetIsSpecifiedActions(property, member);
+
return property;
}
@@ -733,6 +736,25 @@ namespace Newtonsoft.Json.Serialization return o => (bool) shouldSerializeCall(o);
}
+ private void SetIsSpecifiedActions(JsonProperty property, MemberInfo member)
+ {
+ MemberInfo specifiedMember = member.DeclaringType.GetProperty(member.Name + JsonTypeReflector.SpecifiedPostfix);
+ if (specifiedMember == null)
+ specifiedMember = member.DeclaringType.GetField(member.Name + JsonTypeReflector.SpecifiedPostfix);
+
+ if (specifiedMember == null || ReflectionUtils.GetMemberUnderlyingType(specifiedMember) != typeof(bool)
+ || (ReflectionUtils.GetAttribute<XmlIgnoreAttribute>(specifiedMember) != null
+ && ReflectionUtils.GetAttribute<JsonIgnoreAttribute>(specifiedMember) != null))
+ {
+ return;
+ }
+
+ Func<object, object> specifiedPropertyGet = JsonTypeReflector.ReflectionDelegateFactory.CreateGet<object>(specifiedMember);
+
+ property.GetIsSpecified = o => (bool)specifiedPropertyGet(o);
+ property.SetIsSpecified = JsonTypeReflector.ReflectionDelegateFactory.CreateSet<object>(specifiedMember);
+ }
+
/// <summary>
/// Resolves the name of the property.
/// </summary>
diff --git a/Src/Newtonsoft.Json/Serialization/JsonProperty.cs b/Src/Newtonsoft.Json/Serialization/JsonProperty.cs index 13d721c..0b2512e 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonProperty.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonProperty.cs @@ -138,6 +138,18 @@ namespace Newtonsoft.Json.Serialization public Predicate<object> ShouldSerialize { get; set; }
/// <summary>
+ /// Gets or sets a predicate used to determine whether the property should be serialized.
+ /// </summary>
+ /// <value>A predicate used to determine whether the property should be serialized.</value>
+ public Predicate<object> GetIsSpecified { get; set; }
+
+ /// <summary>
+ /// Gets or sets an action used to set whether the property has been deserialized.
+ /// </summary>
+ /// <value>An action used to set whether the property has been deserialized.</value>
+ public Action<object, object> SetIsSpecified { get; set; }
+
+ /// <summary>
/// Returns a <see cref="String"/> that represents this instance.
/// </summary>
/// <returns>
diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index ca80b5d..8ca3954 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -552,7 +552,12 @@ namespace Newtonsoft.Json.Serialization // this could happen because of a JsonConverter against the type
if ((!useExistingValue || value != currentValue)
&& ShouldSetPropertyValue(property, value))
+ {
property.ValueProvider.SetValue(target, value);
+
+ if (property.SetIsSpecified != null)
+ property.SetIsSpecified(target, true);
+ }
}
private bool ShouldSetPropertyValue(JsonProperty property, object value)
diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 526d94b..5b0e75d 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -57,7 +57,8 @@ namespace Newtonsoft.Json.Serialization }
}
- public JsonSerializerInternalWriter(JsonSerializer serializer) : base(serializer)
+ public JsonSerializerInternalWriter(JsonSerializer serializer)
+ : base(serializer)
{
}
@@ -109,7 +110,7 @@ namespace Newtonsoft.Json.Serialization }
else if (valueContract is JsonStringContract)
{
- SerializeString(writer, value, (JsonStringContract) valueContract);
+ SerializeString(writer, value, (JsonStringContract)valueContract);
}
else if (valueContract is JsonObjectContract)
{
@@ -117,12 +118,12 @@ namespace Newtonsoft.Json.Serialization }
else if (valueContract is JsonDictionaryContract)
{
- JsonDictionaryContract dictionaryContract = (JsonDictionaryContract) valueContract;
+ JsonDictionaryContract dictionaryContract = (JsonDictionaryContract)valueContract;
SerializeDictionary(writer, dictionaryContract.CreateWrapper(value), dictionaryContract, member, collectionValueContract);
}
else if (valueContract is JsonArrayContract)
{
- JsonArrayContract arrayContract = (JsonArrayContract) valueContract;
+ JsonArrayContract arrayContract = (JsonArrayContract)valueContract;
SerializeList(writer, arrayContract.CreateWrapper(value), arrayContract, member, collectionValueContract);
}
else if (valueContract is JsonLinqContract)
@@ -132,13 +133,13 @@ namespace Newtonsoft.Json.Serialization #if !SILVERLIGHT && !PocketPC
else if (valueContract is JsonISerializableContract)
{
- SerializeISerializable(writer, (ISerializable) value, (JsonISerializableContract) valueContract);
+ SerializeISerializable(writer, (ISerializable)value, (JsonISerializableContract)valueContract);
}
#endif
#if !(NET35 || NET20 || SILVERLIGHT)
else if (valueContract is JsonDynamicContract)
{
- SerializeDynamic(writer, (IDynamicMetaObjectProvider) value, (JsonDynamicContract) valueContract);
+ SerializeDynamic(writer, (IDynamicMetaObjectProvider)value, (JsonDynamicContract)valueContract);
}
#endif
}
@@ -242,9 +243,9 @@ namespace Newtonsoft.Json.Serialization // use the objectType's TypeConverter if it has one and can convert to a string
if (converter != null
#if !SILVERLIGHT
- && !(converter is ComponentConverter)
+ && !(converter is ComponentConverter)
#endif
- && converter.GetType() != typeof(TypeConverter))
+ && converter.GetType() != typeof(TypeConverter))
{
if (converter.CanConvertTo(typeof(string)))
{
@@ -311,7 +312,7 @@ namespace Newtonsoft.Json.Serialization {
try
{
- if (!property.Ignored && property.Readable && ShouldSerialize(property, value))
+ if (!property.Ignored && property.Readable && ShouldSerialize(property, value) && IsSpecified(property, value))
{
object memberValue = property.ValueProvider.GetValue(value);
JsonContract memberContract = GetContractSafe(memberValue);
@@ -610,5 +611,13 @@ namespace Newtonsoft.Json.Serialization return property.ShouldSerialize(target);
}
+
+ private bool IsSpecified(JsonProperty property, object target)
+ {
+ if (property.GetIsSpecified == null)
+ return true;
+
+ return property.GetIsSpecified(target);
+ }
}
}
\ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs index e1d7261..b9f6493 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs @@ -49,6 +49,7 @@ namespace Newtonsoft.Json.Serialization public const string ArrayValuesPropertyName = "$values";
public const string ShouldSerializePrefix = "ShouldSerialize";
+ public const string SpecifiedPostfix = "Specified";
private static readonly ThreadSafeStore<ICustomAttributeProvider, Type> JsonConverterTypeCache = new ThreadSafeStore<ICustomAttributeProvider, Type>(GetJsonConverterTypeFromAttribute);
#if !SILVERLIGHT && !PocketPC && !NET20
|