From c994fffd182df431a4a0df1ca084edc56705d606 Mon Sep 17 00:00:00 2001 From: "Matt G. Ellis" Date: Sun, 16 Nov 2014 17:38:48 -0800 Subject: [PATCH 1/4] Fix indenting --- Octokit/SimpleJson.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Octokit/SimpleJson.cs b/Octokit/SimpleJson.cs index 07f89085..f0557e1f 100644 --- a/Octokit/SimpleJson.cs +++ b/Octokit/SimpleJson.cs @@ -1356,13 +1356,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 { From 29ae144d8ea5c1fae0a7c836f9e0cba2e9ce0771 Mon Sep 17 00:00:00 2001 From: "Matt G. Ellis" Date: Sun, 16 Nov 2014 20:49:29 -0800 Subject: [PATCH 2/4] Removing unessecary call to MapClrMemberNameToJsonFieldName. The keys in the IDictionary instances in the GetCache are JSON Field names so the call to MapClrMemberNameToJsonFieldName in TrySerializeUnknownTypes is unnesecary at best and incorrect at worst as the input we were passing to that function was not a CLR Member Name but rather a JSON Field Name. --- Octokit/SimpleJson.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Octokit/SimpleJson.cs b/Octokit/SimpleJson.cs index f0557e1f..11433a62 100644 --- a/Octokit/SimpleJson.cs +++ b/Octokit/SimpleJson.cs @@ -1507,7 +1507,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; From 808ffe6081a541c94ae541bd5f960027457371d0 Mon Sep 17 00:00:00 2001 From: "Matt G. Ellis" Date: Mon, 17 Nov 2014 22:49:16 -0800 Subject: [PATCH 3/4] Add richer extension point for CLR to JSON Name Mapping. Instead of just considering the name when mapping a CLR member to a JSON field name, provide a method which gives subclasses the full MemberInfo object. This could be used, for example, to query an attribute on the member to pick up a different name. --- Octokit/SimpleJson.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Octokit/SimpleJson.cs b/Octokit/SimpleJson.cs index 11433a62..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; } From d0dcbe8fb6901c116fed1eeeee06023bda5d6ee3 Mon Sep 17 00:00:00 2001 From: "Matt G. Ellis" Date: Mon, 17 Nov 2014 22:52:01 -0800 Subject: [PATCH 4/4] Allow [Parameter] to control JSON Field Names. Previously, SimpleJsonSerializer.MapClrMemberNameToJsonFieldName special cased the name "Links" since while that was the name of the property in the object model, in JSON "_links" was used instead. It turns out that there was an additional problem, where GitReference wants to expose as Repository, but the name in JSON responses is "repo". Instead of simply adding another special case to MapClrMemberNameToJsonFieldName, we update the implementation of the serializer to allow [Parameter(Key = "some_name")] to denote what name we'd like to use for the field in the JSON object when we serialize. --- Octokit.Tests/SimpleJsonSerializerTests.cs | 3 ++- Octokit/Http/SimpleJsonSerializer.cs | 20 +++++++++++++------- Octokit/Models/Response/Feed.cs | 2 ++ 3 files changed, 17 insertions(+), 8 deletions(-) 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