-Added support for DataContractAttribute and DataMemberAttribute to JsonSerializer

-Added support for serializing private members by marking them with JsonPropertyAttribute or DataMemberAttribute
This commit is contained in:
JamesNK
2009-05-09 10:19:32 +00:00
parent a0a8599d3d
commit 3b26adfdd1
12 changed files with 290 additions and 35 deletions
@@ -292,5 +292,15 @@ now brown cow?", '"', true);
Assert.AreEqual(JsonConvert.NegativeInfinity, JsonConvert.ToString(double.NegativeInfinity));
Assert.AreEqual(JsonConvert.NaN, JsonConvert.ToString(double.NaN));
}
[Test]
public void StringEscaping()
{
string v = @"It's a good day
""sunshine""";
string json = JsonConvert.ToString(v);
Assert.AreEqual(@"""It's a good day\r\n\""sunshine\""""", json);
}
}
}
@@ -25,6 +25,9 @@
using System;
using System.Collections.Generic;
#if !SILVERLIGHT && !PocketPC
using System.ComponentModel.DataAnnotations;
#endif
using System.Text;
using NUnit.Framework;
using Newtonsoft.Json;
@@ -40,6 +43,7 @@ using Newtonsoft.Json.Converters;
using System.Runtime.Serialization.Json;
#endif
using Newtonsoft.Json.Tests.TestObjects;
using System.Runtime.Serialization;
namespace Newtonsoft.Json.Tests
{
@@ -1471,5 +1475,147 @@ keyword such as type of business.""
JsonConvert.SerializeObject(o, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
}
public class RequestOnly
{
public string Request { get; set; }
}
public class NonRequest
{
public Guid Sid { get; set; }
public Guid Uid { get; set; }
public IList<string> FidOrder { get; set; }
}
[Test]
public void PartialClassDeserialize()
{
string json = @"{
""request"": ""ux.settings.update"",
""sid"": ""14c561bd-32a8-457e-b4e5-4bba0832897f"",
""uid"": ""30c39065-0f31-de11-9442-001e3786a8ec"",
""fidOrder"": [
""id"",
""andytest_name"",
""andytest_age"",
""andytest_address"",
""andytest_phone"",
""date"",
""title"",
""titleId""
],
""entityName"": ""Andy Test"",
""setting"": ""entity.field.order""
}";
RequestOnly r = JsonConvert.DeserializeObject<RequestOnly>(json);
Assert.AreEqual("ux.settings.update", r.Request);
NonRequest n = JsonConvert.DeserializeObject<NonRequest>(json);
Assert.AreEqual(new Guid("14c561bd-32a8-457e-b4e5-4bba0832897f"), n.Sid);
Assert.AreEqual(new Guid("30c39065-0f31-de11-9442-001e3786a8ec"), n.Uid);
Assert.AreEqual(8, n.FidOrder.Count);
Assert.AreEqual("id", n.FidOrder[0]);
Assert.AreEqual("titleId", n.FidOrder[n.FidOrder.Count - 1]);
}
#if !SILVERLIGHT && !PocketPC
[MetadataType(typeof(OptInClassMetadata))]
public class OptInClass
{
[DataContract]
public class OptInClassMetadata
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
public string NotIncluded { get; set; }
}
public string Name { get; set; }
public int Age { get; set; }
public string NotIncluded { get; set; }
}
[Test]
public void OptInClassMetadataSerialization()
{
OptInClass optInClass = new OptInClass();
optInClass.Age = 26;
optInClass.Name = "James NK";
optInClass.NotIncluded = "Poor me :(";
string json = JsonConvert.SerializeObject(optInClass, Formatting.Indented);
Assert.AreEqual(@"{
""Name"": ""James NK"",
""Age"": 26
}", json);
OptInClass newOptInClass = JsonConvert.DeserializeObject<OptInClass>(@"{
""Name"": ""James NK"",
""NotIncluded"": ""Ignore me!"",
""Age"": 26
}");
Assert.AreEqual(26, newOptInClass.Age);
Assert.AreEqual("James NK", newOptInClass.Name);
Assert.AreEqual(null, newOptInClass.NotIncluded);
}
#endif
#if !PocketPC
[DataContract]
public class DataContractPrivateMembers
{
public DataContractPrivateMembers()
{
}
public DataContractPrivateMembers(string name, int age, int rank, string title)
{
_name = name;
Age = age;
Rank = rank;
Title = title;
}
[DataMember]
private string _name;
[DataMember(Name = "_age")]
private int Age { get; set; }
[JsonProperty]
private int Rank { get; set; }
[JsonProperty(PropertyName = "JsonTitle")]
[DataMember(Name = "DataTitle")]
private string Title { get; set; }
public string NotIncluded { get; set; }
public override string ToString()
{
return "_name: " + _name + ", _age: " + Age + ", Rank: " + Rank + ", JsonTitle: " + Title;
}
}
[Test]
public void SerializeDataContractPrivateMembers()
{
DataContractPrivateMembers c = new DataContractPrivateMembers("Jeff", 26, 10, "Dr");
c.NotIncluded = "Hi";
string json = JsonConvert.SerializeObject(c, Formatting.Indented);
Assert.AreEqual(@"{
""_name"": ""Jeff"",
""_age"": 26,
""Rank"": 10,
""JsonTitle"": ""Dr""
}", json);
DataContractPrivateMembers cc = JsonConvert.DeserializeObject<DataContractPrivateMembers>(json);
Assert.AreEqual("_name: Jeff, _age: 26, Rank: 10, JsonTitle: Dr", cc.ToString());
}
#endif
}
}
@@ -52,6 +52,7 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
@@ -96,7 +96,7 @@ namespace Newtonsoft.Json.Tests.Serialization
string json = JsonConvert.SerializeObject(privateMembersClass, Formatting.Indented, new JsonSerializerSettings
{
MappingResolver = new CamelCaseMappingResolver { MemberSearchFlags = BindingFlags.NonPublic | BindingFlags.Instance }
MappingResolver = new CamelCaseMappingResolver { DefaultMembersSearchFlags = BindingFlags.NonPublic | BindingFlags.Instance }
});
Assert.AreEqual(@"{
@@ -111,7 +111,7 @@ namespace Newtonsoft.Json.Tests.Serialization
""_internalString"": ""Internal!""
}", new JsonSerializerSettings
{
MappingResolver = new CamelCaseMappingResolver { MemberSearchFlags = BindingFlags.NonPublic | BindingFlags.Instance }
MappingResolver = new CamelCaseMappingResolver { DefaultMembersSearchFlags = BindingFlags.NonPublic | BindingFlags.Instance }
});
Assert.AreEqual("Private!", ReflectionUtils.GetMemberValue(typeof(PrivateMembersClass).GetField("_privateString", BindingFlags.Instance | BindingFlags.NonPublic), deserializedPrivateMembersClass));
@@ -40,5 +40,10 @@ namespace Newtonsoft.Json.Tests
// return utcOffset.Hours.ToString("+00;-00") + utcOffset.Minutes.ToString("00;00");
//}
protected string EscapedJson(string json)
{
return @"@""" + json.Replace(@"""", @"""""") + @"""";
}
}
}
-5
View File
@@ -51,7 +51,6 @@ namespace Newtonsoft.Json
private ObjectCreationHandling _objectCreationHandling;
private NullValueHandling _nullValueHandling;
private DefaultValueHandling _defaultValueHandling;
private int _level;
private JsonConverterCollection _converters;
private IMappingResolver _mappingResolver;
@@ -258,8 +257,6 @@ namespace Newtonsoft.Json
private object CreateObject(JsonReader reader, Type objectType, object existingValue, JsonConverter memberConverter)
{
_level++;
object value;
JsonConverter converter;
@@ -343,8 +340,6 @@ namespace Newtonsoft.Json
}
}
_level--;
return value;
}
@@ -110,6 +110,7 @@
<Compile Include="Schema\UndefinedSchemaIdHandling.cs" />
<Compile Include="Schema\ValidationEventArgs.cs" />
<Compile Include="Schema\ValidationEventHandler.cs" />
<Compile Include="Serialization\CachedAttributeGetter.cs" />
<Compile Include="Serialization\CamelCaseMappingResolver.cs" />
<Compile Include="Serialization\DefaultMappingResolver.cs" />
<Compile Include="Serialization\IMappingResolver.cs" />
@@ -39,6 +39,7 @@
<DocumentationFile>Bin\Release\Newtonsoft.Json.Silverlight.XML</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Windows" />
<Reference Include="mscorlib" />
<Reference Include="system" />
@@ -99,6 +100,7 @@
<Compile Include="ObjectCreationHandling.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ReferenceLoopHandling.cs" />
<Compile Include="Serialization\CachedAttributeGetter.cs" />
<Compile Include="Serialization\CamelCaseMappingResolver.cs" />
<Compile Include="Serialization\DefaultMappingResolver.cs" />
<Compile Include="Serialization\IMappingResolver.cs" />
@@ -76,6 +76,9 @@
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Runtime.Serialization">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
</ItemGroup>
@@ -149,6 +152,7 @@
<Compile Include="Schema\JsonSchemaResolver.cs" />
<Compile Include="Schema\JsonSchemaType.cs" />
<Compile Include="Serialization\JsonTypeReflector.cs" />
<Compile Include="Serialization\CachedAttributeGetter.cs" />
<Compile Include="Utilities\ConvertUtils.cs" />
<Compile Include="Utilities\CollectionWrapper.cs" />
<Compile Include="Utilities\DateTimeUtils.cs" />
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Newtonsoft.Json.Utilities;
namespace Newtonsoft.Json.Serialization
{
internal static class CachedAttributeGetter<T> where T : Attribute
{
private static readonly Dictionary<ICustomAttributeProvider, T> TypeAttributeCache = new Dictionary<ICustomAttributeProvider, T>();
public static T GetAttribute(ICustomAttributeProvider type)
{
T attribute;
if (TypeAttributeCache.TryGetValue(type, out attribute))
return attribute;
// double check locking to avoid threading issues
lock (TypeAttributeCache)
{
if (TypeAttributeCache.TryGetValue(type, out attribute))
return attribute;
attribute = JsonTypeReflector.GetAttribute<T>(type);
TypeAttributeCache[type] = attribute;
return attribute;
}
}
}
}
@@ -28,6 +28,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using Newtonsoft.Json.Utilities;
@@ -39,11 +40,11 @@ namespace Newtonsoft.Json.Serialization
private readonly Dictionary<Type, JsonMemberMappingCollection> TypeMemberMappingsCache = new Dictionary<Type, JsonMemberMappingCollection>();
public BindingFlags MemberSearchFlags { get; set; }
public BindingFlags DefaultMembersSearchFlags { get; set; }
public DefaultMappingResolver()
{
MemberSearchFlags = BindingFlags.Public | BindingFlags.Instance;
DefaultMembersSearchFlags = BindingFlags.Public | BindingFlags.Instance;
}
public virtual JsonMemberMappingCollection ResolveMappings(Type type)
@@ -68,7 +69,32 @@ namespace Newtonsoft.Json.Serialization
protected virtual List<MemberInfo> GetSerializableMembers(Type objectType)
{
return ReflectionUtils.GetFieldsAndProperties(objectType, MemberSearchFlags);
#if !PocketPC
DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(objectType);
#endif
List<MemberInfo> defaultMembers = ReflectionUtils.GetFieldsAndProperties(objectType, DefaultMembersSearchFlags);
List<MemberInfo> allMembers = ReflectionUtils.GetFieldsAndProperties(objectType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
List<MemberInfo> serializableMembers = new List<MemberInfo>();
foreach (MemberInfo member in allMembers)
{
if (defaultMembers.Contains(member))
{
serializableMembers.Add(member);
}
else
{
if (JsonTypeReflector.GetAttribute<JsonPropertyAttribute>(member) != null)
serializableMembers.Add(member);
#if !PocketPC
else if (dataContractAttribute != null && JsonTypeReflector.GetAttribute<DataMemberAttribute>(member) != null)
serializableMembers.Add(member);
#endif
}
}
return serializableMembers;
}
private JsonMemberMappingCollection CreateMemberMappings(Type objectType)
@@ -85,7 +111,8 @@ namespace Newtonsoft.Json.Serialization
{
JsonMemberMapping memberMapping = CreateMemberMapping(memberSerialization, member);
memberMappings.AddMapping(memberMapping);
if (memberMapping != null)
memberMappings.AddMapping(memberMapping);
}
return memberMappings;
@@ -93,19 +120,48 @@ namespace Newtonsoft.Json.Serialization
protected virtual JsonMemberMapping CreateMemberMapping(MemberSerialization memberSerialization, MemberInfo member)
{
#if !PocketPC
DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(member.DeclaringType);
DataMemberAttribute dataMemberAttribute;
if (dataContractAttribute != null)
dataMemberAttribute = JsonTypeReflector.GetAttribute<DataMemberAttribute>(member);
else
dataMemberAttribute = null;
#endif
JsonPropertyAttribute propertyAttribute = JsonTypeReflector.GetAttribute<JsonPropertyAttribute>(member);
bool hasIgnoreAttribute = (JsonTypeReflector.GetAttribute<JsonIgnoreAttribute>(member) != null);
string mappedName = (propertyAttribute != null && propertyAttribute.PropertyName != null)
? propertyAttribute.PropertyName
: member.Name;
string mappedName;
if (propertyAttribute != null && propertyAttribute.PropertyName != null)
mappedName = propertyAttribute.PropertyName;
#if !PocketPC
else if (dataMemberAttribute != null && dataMemberAttribute.Name != null)
mappedName = dataMemberAttribute.Name;
#endif
else
mappedName = member.Name;
string resolvedMappedName = ResolveMappingName(mappedName);
bool required = (propertyAttribute != null) ? propertyAttribute.IsRequired : false;
bool required;
if (propertyAttribute != null)
required = propertyAttribute.IsRequired;
#if !PocketPC
else if (dataMemberAttribute != null)
required = dataMemberAttribute.IsRequired;
#endif
else
required = false;
bool ignored = (hasIgnoreAttribute
|| (memberSerialization == MemberSerialization.OptIn && propertyAttribute == null));
bool ignored = (hasIgnoreAttribute ||
(memberSerialization == MemberSerialization.OptIn
&& propertyAttribute == null
#if !PocketPC
&& dataMemberAttribute == null
#endif
));
bool readable = ReflectionUtils.CanReadMemberValue(member);
bool writable = ReflectionUtils.CanSetMemberValue(member);
@@ -31,6 +31,7 @@ using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using Newtonsoft.Json.Utilities;
namespace Newtonsoft.Json.Serialization
@@ -38,36 +39,36 @@ namespace Newtonsoft.Json.Serialization
internal static class JsonTypeReflector
{
private static readonly Dictionary<ICustomAttributeProvider, Type> ConverterTypeCache = new Dictionary<ICustomAttributeProvider, Type>();
private static readonly Dictionary<Type, JsonContainerAttribute> TypeContainerAttributeCache = new Dictionary<Type, JsonContainerAttribute>();
private static readonly Dictionary<Type, Type> AssociatedMetadataTypesCache = new Dictionary<Type, Type>();
public static JsonContainerAttribute GetJsonContainerAttribute(Type type)
{
JsonContainerAttribute containerAttribute;
if (TypeContainerAttributeCache.TryGetValue(type, out containerAttribute))
return containerAttribute;
// double check locking to avoid threading issues
lock (TypeContainerAttributeCache)
{
if (TypeContainerAttributeCache.TryGetValue(type, out containerAttribute))
return containerAttribute;
containerAttribute = ReflectionUtils.GetAttribute<JsonContainerAttribute>(type);
TypeContainerAttributeCache[type] = containerAttribute;
return containerAttribute;
}
return CachedAttributeGetter<JsonContainerAttribute>.GetAttribute(type);
}
#if !PocketPC
public static DataContractAttribute GetDataContractAttribute(Type type)
{
return CachedAttributeGetter<DataContractAttribute>.GetAttribute(type);
}
#endif
public static MemberSerialization GetObjectMemberSerialization(Type objectType)
{
JsonObjectAttribute objectAttribute = GetJsonContainerAttribute(objectType) as JsonObjectAttribute;
if (objectAttribute == null)
{
#if !PocketPC
DataContractAttribute dataContractAttribute = GetDataContractAttribute(objectType);
if (dataContractAttribute != null)
return MemberSerialization.OptIn;
#endif
return MemberSerialization.OptOut;
}
return objectAttribute.MemberSerialization;
}