From e4699185962537b22f65b18306b98fa39a9c06f1 Mon Sep 17 00:00:00 2001 From: JamesNK Date: Sat, 29 Mar 2008 10:27:44 +0000 Subject: [PATCH] Added opt-in, opt-out serialization option Added virtual GetSerializableMembers to JsonSerializer --- .../JsonSerializerTest.cs | 38 +++++++++++++ .../XmlNodeConverterTest.cs | 42 ++++++++++++++- .../Converters/XmlNodeConverter.cs | 2 +- Src/Newtonsoft.Json/JsonObjectAttribute.cs | 53 +++++++++++++++++++ Src/Newtonsoft.Json/JsonSerializer.cs | 37 ++++++++++--- Src/Newtonsoft.Json/MemberSerialization.cs | 38 +++++++++++++ Src/Newtonsoft.Json/Newtonsoft.Json.csproj | 2 + 7 files changed, 202 insertions(+), 10 deletions(-) create mode 100644 Src/Newtonsoft.Json/JsonObjectAttribute.cs create mode 100644 Src/Newtonsoft.Json/MemberSerialization.cs diff --git a/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs index e5964621..1cf018f2 100644 --- a/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs @@ -941,5 +941,43 @@ keyword such as type of business."" StringComparison s = JavaScriptConvert.DeserializeObject(json); Assert.AreEqual(StringComparison.CurrentCultureIgnoreCase, s); } + + [JsonObject(MemberSerialization.OptIn)] + public class JsonIgnoreAttributeOnClassTestClass + { + private int _property = 21; + private int _ignoredProperty = 12; + + [JsonProperty("TheField")] + public int Field; + + [JsonProperty] + public int Property + { + get { return _property; } + } + + public int IgnoredField; + + [JsonProperty] + [JsonIgnore] // JsonIgnore should take priority + public int IgnoredProperty + { + get { return _ignoredProperty; } + } + } + + [Test] + public void JsonIgnoreAttributeOnClassTest() + { + string json = JavaScriptConvert.SerializeObject(new JsonIgnoreAttributeOnClassTestClass()); + + Assert.AreEqual(@"{""TheField"":0,""Property"":21}", json); + + JsonIgnoreAttributeOnClassTestClass c = JavaScriptConvert.DeserializeObject(@"{""TheField"":99,""Property"":-1,""IgnoredField"":-1}"); + + Assert.AreEqual(0, c.IgnoredField); + Assert.AreEqual(99, c.Field); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/XmlNodeConverterTest.cs b/Src/Newtonsoft.Json.Tests/XmlNodeConverterTest.cs index b5d93b0e..5e7c4e2b 100644 --- a/Src/Newtonsoft.Json.Tests/XmlNodeConverterTest.cs +++ b/Src/Newtonsoft.Json.Tests/XmlNodeConverterTest.cs @@ -347,7 +347,7 @@ namespace Newtonsoft.Json.Tests } [Test] - [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "JSON root object has multiple properties.")] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "JSON root object has multiple properties. The root object must have a single property in order to create a valid XML document.")] public void RootObjectMultipleProperties() { XmlDocument newDoc = (XmlDocument)JavaScriptConvert.DeserializeXmlNode(@"{Prop1:1,Prop2:2}"); @@ -414,5 +414,45 @@ namespace Newtonsoft.Json.Tests arrayJsonText = JavaScriptConvert.SerializeXmlNode(arrayDoc); Assert.AreEqual(@"{""root"":{""person"":{""@id"":""1"",""name"":""Alan"",""url"":""http://www.google.com"",""role"":""Admin1""}}}", arrayJsonText); } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "JSON root object has multiple properties. The root object must have a single property in order to create a valid XML document.")] + public void MultipleRootProperties() + { + string strJSON = @"{ +""count"": 773840,""photos"": [ +{ +""photo_id"": 532693, +""photo_title"": ""Wheatfield in afternoon light"", +""photo_url"": ""http://www.panoramio.com/photo/532693"", +""photofileurl"": ""http://static2.bareka.com/photos/medium/532693.jpg"", +""longitude"": 11.280727, +""latitude"": 59.643198, +""width"": 500, +""height"": 333, +""upload_date"": ""22 January 2007"", +""owner_id"": 39160, +""owner_name"": ""Snemann"", +""owner_url"": ""http://www.panoramio.com/user/39160"", +}, +{ +""photo_id"": 505229, +""photo_title"": ""Etangs près de Dijon"", +""photo_url"": ""http://www.panoramio.com/photo/505229"", +""photofileurl"": ""http://static2.bareka.com/photos/medium/505229.jpg"", +""longitude"": 5.168552, +""latitude"": 47.312642, +""width"": 350, +""height"": 500, +""upload_date"": ""20 January 2007"", +""owner_id"": 78506, +""owner_name"": ""Philippe Stoop"", +""owner_url"": ""http://www.panoramio.com/user/78506"" +} +] +}"; + + XmlDocument newDoc = (XmlDocument)JavaScriptConvert.DeserializeXmlNode(strJSON); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs index 0ff6a1f2..bac427d8 100644 --- a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs +++ b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs @@ -444,7 +444,7 @@ namespace Newtonsoft.Json.Converters { case JsonToken.PropertyName: if (currentNode.NodeType == XmlNodeType.Document && document.DocumentElement != null) - throw new JsonSerializationException("JSON root object has multiple properties."); + throw new JsonSerializationException("JSON root object has multiple properties. The root object must have a single property in order to create a valid XML document."); string propertyName = reader.Value.ToString(); reader.Read(); diff --git a/Src/Newtonsoft.Json/JsonObjectAttribute.cs b/Src/Newtonsoft.Json/JsonObjectAttribute.cs new file mode 100644 index 00000000..0c172cae --- /dev/null +++ b/Src/Newtonsoft.Json/JsonObjectAttribute.cs @@ -0,0 +1,53 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] + public sealed class JsonObjectAttribute : Attribute + { + private MemberSerialization _memberSerialization = MemberSerialization.OptOut; + + public MemberSerialization MemberSerialization + { + get { return _memberSerialization; } + set { _memberSerialization = value; } + } + + public JsonObjectAttribute() + { + } + + public JsonObjectAttribute(MemberSerialization memberSerialization) + { + MemberSerialization = memberSerialization; + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index 69460512..7ff34d07 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -321,20 +321,26 @@ namespace Newtonsoft.Json private MemberMappingCollection CreateMemberMappings(Type objectType) { - List members = ReflectionUtils.GetFieldsAndProperties(objectType, BindingFlags.Public | BindingFlags.Instance); + MemberSerialization memberSerialization = GetObjectMemberSerialization(objectType); + + List members = GetSerializableMembers(objectType); + if (members == null) + throw new JsonSerializationException("Null collection of seralizable members returned."); + MemberMappingCollection memberMappings = new MemberMappingCollection(); foreach (MemberInfo member in members) { - string mappedName; - JsonPropertyAttribute propertyAttribute = ReflectionUtils.GetAttribute(member, true); - if (propertyAttribute != null) - mappedName = propertyAttribute.PropertyName; - else - mappedName = member.Name; + bool hasIgnoreAttribute = member.IsDefined(typeof(JsonIgnoreAttribute), true); + + string mappedName = (propertyAttribute != null && propertyAttribute.PropertyName != null) + ? propertyAttribute.PropertyName + : member.Name; + + bool ignored = (hasIgnoreAttribute + || (memberSerialization == MemberSerialization.OptIn && propertyAttribute == null)); - bool ignored = member.IsDefined(typeof(JsonIgnoreAttribute), true); bool readable = ReflectionUtils.CanReadMemberValue(member); bool writable = ReflectionUtils.CanSetMemberValue(member); MemberMapping memberMapping = new MemberMapping(mappedName, member, ignored, readable, writable); @@ -345,6 +351,21 @@ namespace Newtonsoft.Json return memberMappings; } + protected virtual List GetSerializableMembers(Type objectType) + { + return ReflectionUtils.GetFieldsAndProperties(objectType, BindingFlags.Public | BindingFlags.Instance); + } + + private static MemberSerialization GetObjectMemberSerialization(Type objectType) + { + JsonObjectAttribute objectAttribute = ReflectionUtils.GetAttribute(objectType, true); + + if (objectAttribute == null) + return MemberSerialization.OptOut; + else + return objectAttribute.MemberSerialization; + } + private void SetObjectMember(JsonReader reader, object target, Type targetType, string memberName) { if (!reader.Read()) diff --git a/Src/Newtonsoft.Json/MemberSerialization.cs b/Src/Newtonsoft.Json/MemberSerialization.cs new file mode 100644 index 00000000..9a3ad0e4 --- /dev/null +++ b/Src/Newtonsoft.Json/MemberSerialization.cs @@ -0,0 +1,38 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json +{ + public enum MemberSerialization + { + OptOut, + OptIn + } +} diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index a04a07c2..ff3ec3ac 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -75,6 +75,8 @@ + +