diff --git a/Octokit.Tests/SimpleJsonSerializerTests.cs b/Octokit.Tests/SimpleJsonSerializerTests.cs index 2e6ae9b1..0956ea59 100644 --- a/Octokit.Tests/SimpleJsonSerializerTests.cs +++ b/Octokit.Tests/SimpleJsonSerializerTests.cs @@ -96,15 +96,21 @@ namespace Octokit.Tests } [Fact] - public void UnencodesBase64Strings() + public void DeserializesProtectedProperties() { - const string json = "{\"name\":\"RmVycmlzIEJ1ZWxsZXI=\",\"description\":\"stuff\",\"content\":\"RGF5IG9mZg==\"}"; - - var someObject = new SimpleJsonSerializer().Deserialize(json); + const string json = "{\"content\":\"hello\"}"; - Assert.Equal("Ferris Bueller", someObject.Name); - Assert.Equal("Day off", someObject.Content); - Assert.Equal("stuff", someObject.Description); + var someObject = new SimpleJsonSerializer().Deserialize(json); + + Assert.Equal("*hello*", someObject.Content); + } + + public class AnotherObject + { + [Parameter(Key = "content")] + protected string EncodedContent { get; set; } + + public string Content { get { return "*" + EncodedContent + "*"; } } } [Fact] diff --git a/Octokit/Clients/RepositoryContentsClient.cs b/Octokit/Clients/RepositoryContentsClient.cs index a9d67858..aa9fdc2b 100644 --- a/Octokit/Clients/RepositoryContentsClient.cs +++ b/Octokit/Clients/RepositoryContentsClient.cs @@ -30,7 +30,6 @@ namespace Octokit var url = ApiUrls.RepositoryContent(owner, name, path); return await ApiConnection.GetAll(url); - // return new List { await ApiConnection.Get(url) } } /// diff --git a/Octokit/Helpers/PropertyOrField.cs b/Octokit/Helpers/PropertyOrField.cs index 703b157c..af3e7870 100644 --- a/Octokit/Helpers/PropertyOrField.cs +++ b/Octokit/Helpers/PropertyOrField.cs @@ -19,6 +19,8 @@ namespace Octokit CanWrite = propertyInfo.CanWrite; IsStatic = ReflectionUtils.GetGetterMethodInfo(propertyInfo).IsStatic; IsPublic = ReflectionUtils.GetGetterMethodInfo(propertyInfo).IsPublic; + + CanDeserialize = (IsPublic || HasParameterAttribute) && !IsStatic && CanWrite; } public PropertyOrField(FieldInfo fieldInfo) : this((MemberInfo)fieldInfo) @@ -29,6 +31,8 @@ namespace Octokit CanWrite = true; IsStatic = fieldInfo.IsStatic; IsPublic = fieldInfo.IsPublic; + + CanDeserialize = (IsPublic || HasParameterAttribute) && !IsStatic && CanWrite && !fieldInfo.IsInitOnly; } protected PropertyOrField(MemberInfo memberInfo) @@ -36,6 +40,7 @@ namespace Octokit MemberInfo = memberInfo; Base64Encoded = memberInfo.GetCustomAttribute() != null; SerializeNull = memberInfo.GetCustomAttribute() != null; + HasParameterAttribute = memberInfo.GetCustomAttribute() != null; } public bool CanRead { get; private set; } @@ -50,6 +55,8 @@ namespace Octokit public bool IsPublic { get; private set; } + public bool HasParameterAttribute { get; private set; } + public MemberInfo MemberInfo { get; private set; } public object GetValue(object instance) @@ -75,5 +82,44 @@ namespace Octokit } throw new InvalidOperationException("Property and Field cannot both be null"); } + + public string JsonFieldName + { + get { return MemberInfo.GetJsonFieldName(); } + } + + public ReflectionUtils.SetDelegate SetDelegate + { + get + { + if (_propertyInfo != null) + { + return ReflectionUtils.GetSetMethod(_propertyInfo); + } + if (_fieldInfo != null) + { + return ReflectionUtils.GetSetMethod(_fieldInfo); + } + throw new InvalidOperationException("Property and Field cannot both be null"); + } + } + + public Type Type + { + get + { + if (_propertyInfo != null) + { + return _propertyInfo.PropertyType; + } + if (_fieldInfo != null) + { + return _fieldInfo.FieldType; + } + throw new InvalidOperationException("Property and Field cannot both be null"); + } + } + + public bool CanDeserialize { get; set; } } } diff --git a/Octokit/Helpers/ReflectionExtensions.cs b/Octokit/Helpers/ReflectionExtensions.cs index 945a53a0..01c07b12 100644 --- a/Octokit/Helpers/ReflectionExtensions.cs +++ b/Octokit/Helpers/ReflectionExtensions.cs @@ -2,17 +2,31 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using Octokit.Internal; using Octokit.Reflection; namespace Octokit { internal static class ReflectionExtensions { + public static string GetJsonFieldName(this MemberInfo memberInfo) + { + var memberName = memberInfo.Name; + var paramAttr = memberInfo.GetCustomAttribute(); + + if (paramAttr != null && !string.IsNullOrEmpty(paramAttr.Key)) + { + memberName = paramAttr.Key; + } + + return memberName.ToRubyCase(); + } + public static IEnumerable GetPropertiesAndFields(this Type type) { return ReflectionUtils.GetProperties(type).Select(property => new PropertyOrField(property)) .Union(ReflectionUtils.GetFields(type).Select(field => new PropertyOrField(field))) - .Where(p => p.IsPublic && !p.IsStatic); + .Where(p => (p.IsPublic || p.HasParameterAttribute) && !p.IsStatic); } public static bool IsDateTimeOffset(this Type type) diff --git a/Octokit/Http/SimpleJsonSerializer.cs b/Octokit/Http/SimpleJsonSerializer.cs index 1cd3e7c0..ff95c4ec 100644 --- a/Octokit/Http/SimpleJsonSerializer.cs +++ b/Octokit/Http/SimpleJsonSerializer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Reflection; using Octokit.Reflection; @@ -27,15 +28,7 @@ namespace Octokit.Internal protected override string MapClrMemberToJsonFieldName(MemberInfo member) { - var memberName = member.Name; - var paramAttr = member.GetCustomAttribute(); - - if (paramAttr != null && !string.IsNullOrEmpty(paramAttr.Key)) - { - memberName = paramAttr.Key; - } - - return memberName.ToRubyCase(); + return member.GetJsonFieldName(); } internal override IDictionary GetterValueFactory(Type type) @@ -141,25 +134,16 @@ namespace Octokit.Internal } } - var deserialized = base.DeserializeObject(value, type); + return base.DeserializeObject(value, type); + } - // Handle base64 encoding - foreach (var propertyInfo in type.GetPropertiesAndFields()) - { - if (!propertyInfo.CanRead) continue; - if (!propertyInfo.CanWrite) continue; - if (propertyInfo.Base64Encoded) - { - var propertyValue = propertyInfo.GetValue(deserialized) as string; - if (propertyValue != null) - { - var unencoded = propertyValue.FromBase64String(); - propertyInfo.SetValue(deserialized, unencoded); - } - } - } - - return deserialized; + internal override IDictionary> SetterValueFactory(Type type) + { + return type.GetPropertiesAndFields() + .Where(p => p.CanDeserialize) + .ToDictionary( + p => p.JsonFieldName, + p => new KeyValuePair(p.Type, p.SetDelegate)); } } } diff --git a/Octokit/Models/Response/RepositoryContent.cs b/Octokit/Models/Response/RepositoryContent.cs index 5c65e1f4..1ddf508b 100644 --- a/Octokit/Models/Response/RepositoryContent.cs +++ b/Octokit/Models/Response/RepositoryContent.cs @@ -1,4 +1,5 @@ using System; +using Octokit.Internal; namespace Octokit { @@ -16,7 +17,13 @@ namespace Octokit /// /// The encoded content if this is a file. Otherwise it's null. /// - public string Content { get; set; } + [Parameter(Key = "content")] + protected string EncodedContent { get; set; } + + public string Content + { + get { return EncodedContent.FromBase64String(); } + } /// /// Path to the target file in the repository if this is a symlink. Otherwise it's null.