diff --git a/Octokit.Tests/SimpleJsonSerializerTests.cs b/Octokit.Tests/SimpleJsonSerializerTests.cs index 6a7d3327..499d26c2 100644 --- a/Octokit.Tests/SimpleJsonSerializerTests.cs +++ b/Octokit.Tests/SimpleJsonSerializerTests.cs @@ -135,7 +135,7 @@ namespace Octokit.Tests } [Fact] - public void IgnoresUnderscore() + public void RespectsParameterKeyName() { const string json = "{\"_links\":\"blah\"}"; @@ -151,6 +151,7 @@ namespace Octokit.Tests public string FirstName { get; set; } public bool IsSomething { get; set; } public bool Private { get; set; } + [Parameter(Key = "_links")] public string Links { get; set; } } } diff --git a/Octokit/Http/SimpleJsonSerializer.cs b/Octokit/Http/SimpleJsonSerializer.cs index 91d54df9..3c879970 100644 --- a/Octokit/Http/SimpleJsonSerializer.cs +++ b/Octokit/Http/SimpleJsonSerializer.cs @@ -26,11 +26,17 @@ namespace Octokit.Internal readonly List _membersWhichShouldPublishNull = new List(); - protected override string MapClrMemberNameToJsonFieldName(string clrPropertyName) + protected override string MapClrMemberToJsonFieldName(MemberInfo member) { - var rubyCased = clrPropertyName.ToRubyCase(); - if (rubyCased == "links") return "_links"; // Special case for GitHub API - return rubyCased; + var memberName = member.Name; + var paramAttr = member.GetCustomAttribute(); + + if (paramAttr != null && !string.IsNullOrEmpty(paramAttr.Key)) + { + memberName = paramAttr.Key; + } + + return memberName.ToRubyCase(); } internal override IDictionary GetterValueFactory(Type type) @@ -53,7 +59,7 @@ namespace Octokit.Internal var attribute = propertyInfo.GetCustomAttribute(); if (attribute == null) continue; - _membersWhichShouldPublishNull.Add(fullName + MapClrMemberNameToJsonFieldName(propertyInfo.Name)); + _membersWhichShouldPublishNull.Add(fullName + MapClrMemberToJsonFieldName(propertyInfo)); } foreach (var fieldInfo in ReflectionUtils.GetFields(type)) { @@ -62,7 +68,7 @@ namespace Octokit.Internal var attribute = fieldInfo.GetCustomAttribute(); if (attribute == null) continue; - _membersWhichShouldPublishNull.Add(fullName + MapClrMemberNameToJsonFieldName(fieldInfo.Name)); + _membersWhichShouldPublishNull.Add(fullName + MapClrMemberToJsonFieldName(fieldInfo)); } return base.GetterValueFactory(type); @@ -89,7 +95,7 @@ namespace Octokit.Internal continue; } - jsonObject.Add(MapClrMemberNameToJsonFieldName(getter.Key), value); + jsonObject.Add(getter.Key, value); } } output = jsonObject; diff --git a/Octokit/Models/Response/Feed.cs b/Octokit/Models/Response/Feed.cs index 5dced3a5..8a517cc2 100644 --- a/Octokit/Models/Response/Feed.cs +++ b/Octokit/Models/Response/Feed.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Threading.Tasks; +using Octokit.Internal; namespace Octokit { @@ -45,6 +46,7 @@ namespace Octokit /// /// List of feed urls including feed url and feed type, e.g. application/atom+xml /// + [Parameter(Key = "_links")] public FeedLinks Links { get; set; } internal string DebuggerDisplay diff --git a/Octokit/SimpleJson.cs b/Octokit/SimpleJson.cs index 07f89085..24fe554b 100644 --- a/Octokit/SimpleJson.cs +++ b/Octokit/SimpleJson.cs @@ -1265,6 +1265,11 @@ namespace Octokit SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); } + protected virtual string MapClrMemberToJsonFieldName(MemberInfo member) + { + return MapClrMemberNameToJsonFieldName(member.Name); + } + protected virtual string MapClrMemberNameToJsonFieldName(string clrPropertyName) { return clrPropertyName; @@ -1285,14 +1290,14 @@ namespace Octokit MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); if (getMethod.IsStatic || !getMethod.IsPublic) continue; - result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = ReflectionUtils.GetGetMethod(propertyInfo); + result[MapClrMemberToJsonFieldName(propertyInfo)] = ReflectionUtils.GetGetMethod(propertyInfo); } } foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) { if (fieldInfo.IsStatic || !fieldInfo.IsPublic) continue; - result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = ReflectionUtils.GetGetMethod(fieldInfo); + result[MapClrMemberToJsonFieldName(fieldInfo)] = ReflectionUtils.GetGetMethod(fieldInfo); } return result; } @@ -1307,14 +1312,14 @@ namespace Octokit MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); if (setMethod.IsStatic || !setMethod.IsPublic) continue; - result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); + result[MapClrMemberToJsonFieldName(propertyInfo)] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); } } foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) { if (fieldInfo.IsInitOnly || fieldInfo.IsStatic || !fieldInfo.IsPublic) continue; - result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); + result[MapClrMemberToJsonFieldName(fieldInfo)] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); } return result; } @@ -1356,13 +1361,13 @@ namespace Octokit if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result)) return result; - return null; + return null; } - if (type == typeof(string)) - return str; + if (type == typeof(string)) + return str; - return Convert.ChangeType(str, type, CultureInfo.InvariantCulture); + return Convert.ChangeType(str, type, CultureInfo.InvariantCulture); } else { @@ -1507,7 +1512,7 @@ namespace Octokit foreach (KeyValuePair getter in getters) { if (getter.Value != null) - obj.Add(MapClrMemberNameToJsonFieldName(getter.Key), getter.Value(input)); + obj.Add(getter.Key, getter.Value(input)); } output = obj; return true;