Skip to content
This repository was archived by the owner on Dec 5, 2024. It is now read-only.

Commit 891d1bc

Browse files
committed
Add more options to SimpleJson
Add extension methods for serializing/deserializing so we can control how fields and properties are named. This allows us to serialize/deserialize public and private fields and properties with either Capitalized or lowerCase names
1 parent 467fe02 commit 891d1bc

File tree

6 files changed

+163
-29
lines changed

6 files changed

+163
-29
lines changed

script

src/GitHub.Api/Authentication/Keychain.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ private void LoadConnectionsFromDisk()
198198
var json = cachePath.ReadAllText();
199199
try
200200
{
201-
var conns = SimpleJson.DeserializeObject<Connection[]>(json);
201+
var conns = json.FromJson<Connection[]>();
202202
UpdateConnections(conns);
203203
}
204204
catch (IOException ex)
@@ -219,7 +219,7 @@ private void SaveConnectionsToDisk(bool raiseChangedEvent = true)
219219
//logger.Trace("WriteCacheToDisk Count:{0} Path:{1}", connectionCache.Count, cachePath.ToString());
220220
try
221221
{
222-
var json = SimpleJson.SerializeObject(connections.Values.ToArray());
222+
var json = connections.Values.ToJson();
223223
cachePath.WriteAllText(json);
224224
}
225225
catch (IOException ex)

src/GitHub.Api/Helpers/SimpleJson.cs

Lines changed: 154 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
// NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke().
3838
// define if you are using .net framework <= 3.0 or < WP7.5
39-
//#define SIMPLE_JSON_NO_LINQ_EXPRESSION
39+
#define SIMPLE_JSON_NO_LINQ_EXPRESSION
4040

4141
// NOTE: uncomment the following line if you are compiling under Window Metro style application/library.
4242
// usually already defined in properties
@@ -66,12 +66,14 @@
6666
using System.Reflection;
6767
using System.Runtime.Serialization;
6868
using System.Text;
69-
using GitHub.Reflection;
69+
using System.Diagnostics;
70+
using System.Runtime.CompilerServices;
71+
using GitHub.Unity.Json;
7072

7173
// ReSharper disable LoopCanBeConvertedToQuery
7274
// ReSharper disable RedundantExplicitArrayCreation
7375
// ReSharper disable SuggestUseVarKeywordEvident
74-
namespace GitHub
76+
namespace GitHub.Unity.Json
7577
{
7678
/// <summary>
7779
/// Represents the json array.
@@ -482,10 +484,7 @@ public override IEnumerable<string> GetDynamicMemberNames()
482484
}
483485
#endif
484486
}
485-
}
486487

487-
namespace GitHub
488-
{
489488
/// <summary>
490489
/// This class encodes and decodes JSON strings.
491490
/// Spec. details, see http://www.json.org/
@@ -517,6 +516,7 @@ static class SimpleJson
517516

518517
private static readonly char[] EscapeTable;
519518
private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' };
519+
private static readonly string EscapeCharactersString = new string(EscapeCharacters);
520520

521521
static SimpleJson()
522522
{
@@ -620,7 +620,7 @@ public static string EscapeToJavascriptString(string jsonString)
620620
StringBuilder sb = new StringBuilder();
621621
char c;
622622

623-
for (int i = 0; i < jsonString.Length; )
623+
for (int i = 0; i < jsonString.Length;)
624624
{
625625
c = jsonString[i++];
626626

@@ -1282,14 +1282,14 @@ internal virtual ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(T
12821282
if (propertyInfo.CanRead)
12831283
{
12841284
MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo);
1285-
if (getMethod.IsStatic || !getMethod.IsPublic)
1285+
if (!CanAddProperty(propertyInfo, getMethod))
12861286
continue;
12871287
result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = ReflectionUtils.GetGetMethod(propertyInfo);
12881288
}
12891289
}
12901290
foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type))
12911291
{
1292-
if (fieldInfo.IsStatic || !fieldInfo.IsPublic)
1292+
if (!CanAddField(fieldInfo))
12931293
continue;
12941294
result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = ReflectionUtils.GetGetMethod(fieldInfo);
12951295
}
@@ -1304,20 +1304,40 @@ internal virtual ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(T
13041304
if (propertyInfo.CanWrite)
13051305
{
13061306
MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo);
1307-
if (setMethod.IsStatic || !setMethod.IsPublic)
1307+
if (!CanAddProperty(propertyInfo, setMethod))
13081308
continue;
13091309
result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = new KeyValuePair<Type, ReflectionUtils.SetDelegate>(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo));
13101310
}
13111311
}
13121312
foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type))
13131313
{
1314-
if (fieldInfo.IsInitOnly || fieldInfo.IsStatic || !fieldInfo.IsPublic)
1314+
if (fieldInfo.IsInitOnly || !CanAddField(fieldInfo))
13151315
continue;
13161316
result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = new KeyValuePair<Type, ReflectionUtils.SetDelegate>(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo));
13171317
}
13181318
return result;
13191319
}
13201320

1321+
protected virtual bool CanAddField(FieldInfo field)
1322+
{
1323+
if (field.IsStatic)
1324+
return false;
1325+
if (ReflectionUtils.GetAttribute(field, typeof(NotSerializedAttribute)) != null)
1326+
return false;
1327+
if (ReflectionUtils.GetAttribute(field, typeof(CompilerGeneratedAttribute)) != null)
1328+
return false;
1329+
return true;
1330+
}
1331+
1332+
protected virtual bool CanAddProperty(PropertyInfo property, MethodInfo method)
1333+
{
1334+
if (method.IsStatic)
1335+
return false;
1336+
if (ReflectionUtils.GetAttribute(property, typeof(NotSerializedAttribute)) != null)
1337+
return false;
1338+
return true;
1339+
}
1340+
13211341
public virtual bool TrySerializeNonPrimitiveObject(object input, out object output)
13221342
{
13231343
return TrySerializeKnownTypes(input, out output) || TrySerializeUnknownTypes(input, out output);
@@ -1329,7 +1349,7 @@ public virtual object DeserializeObject(object value, Type type)
13291349
if (type == null) throw new ArgumentNullException("type");
13301350
string str = value as string;
13311351

1332-
if (type == typeof (Guid) && string.IsNullOrEmpty(str))
1352+
if (type == typeof(Guid) && string.IsNullOrEmpty(str))
13331353
return default(Guid);
13341354

13351355
if (value == null)
@@ -1349,19 +1369,19 @@ public virtual object DeserializeObject(object value, Type type)
13491369
return new Guid(str);
13501370
if (type == typeof(Uri))
13511371
{
1352-
bool isValid = Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute);
1372+
bool isValid = Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute);
13531373

13541374
Uri result;
13551375
if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result))
13561376
return result;
13571377

1358-
return null;
1378+
return null;
13591379
}
13601380

1361-
if (type == typeof(string))
1362-
return str;
1381+
if (type == typeof(string))
1382+
return str;
13631383

1364-
return Convert.ChangeType(str, type, CultureInfo.InvariantCulture);
1384+
return Convert.ChangeType(str, type, CultureInfo.InvariantCulture);
13651385
}
13661386
else
13671387
{
@@ -1592,8 +1612,6 @@ private static bool CanAdd(MemberInfo info, out string jsonKey)
15921612

15931613
#endif
15941614

1595-
namespace Reflection
1596-
{
15971615
// This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules
15981616
// that might be in place in the target project.
15991617
[GeneratedCode("reflection-utils", "1.0.0")]
@@ -1648,7 +1666,7 @@ public static Type GetGenericListElementType(Type type)
16481666
foreach (Type implementedInterface in interfaces)
16491667
{
16501668
if (IsTypeGeneric(implementedInterface) &&
1651-
implementedInterface.GetGenericTypeDefinition() == typeof (IList<>))
1669+
implementedInterface.GetGenericTypeDefinition() == typeof(IList<>))
16521670
{
16531671
return GetGenericTypeArguments(implementedInterface)[0];
16541672
}
@@ -1837,7 +1855,13 @@ public static ConstructorDelegate GetConstructorByReflection(ConstructorInfo con
18371855
public static ConstructorDelegate GetConstructorByReflection(Type type, params Type[] argsType)
18381856
{
18391857
ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType);
1840-
return constructorInfo == null ? null : GetConstructorByReflection(constructorInfo);
1858+
// if it's a value type (i.e., struct), it won't have a default constructor, so use Activator instead
1859+
return constructorInfo == null ? (type.IsValueType ? GetConstructorForValueType(type) : null) : GetConstructorByReflection(constructorInfo);
1860+
}
1861+
1862+
static ConstructorDelegate GetConstructorForValueType(Type type)
1863+
{
1864+
return delegate(object[] args) { return Activator.CreateInstance(type); };
18411865
}
18421866

18431867
#if !SIMPLE_JSON_NO_LINQ_EXPRESSION
@@ -1864,7 +1888,8 @@ public static ConstructorDelegate GetConstructorByExpression(ConstructorInfo con
18641888
public static ConstructorDelegate GetConstructorByExpression(Type type, params Type[] argsType)
18651889
{
18661890
ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType);
1867-
return constructorInfo == null ? null : GetConstructorByExpression(constructorInfo);
1891+
// if it's a value type (i.e., struct), it won't have a default constructor, so use Activator instead
1892+
return constructorInfo == null ? (type.IsValueType ? GetConstructorForValueType(type) : null) : GetConstructorByExpression(constructorInfo);
18681893
}
18691894

18701895
#endif
@@ -1924,6 +1949,9 @@ public static SetDelegate GetSetMethod(PropertyInfo propertyInfo)
19241949
#if SIMPLE_JSON_NO_LINQ_EXPRESSION
19251950
return GetSetMethodByReflection(propertyInfo);
19261951
#else
1952+
// if it's a struct, we want to use reflection, as linq expressions modify copies of the object and not the real thing
1953+
if (propertyInfo.DeclaringType.IsValueType)
1954+
return GetSetMethodByReflection(propertyInfo);
19271955
return GetSetMethodByExpression(propertyInfo);
19281956
#endif
19291957
}
@@ -1933,6 +1961,9 @@ public static SetDelegate GetSetMethod(FieldInfo fieldInfo)
19331961
#if SIMPLE_JSON_NO_LINQ_EXPRESSION
19341962
return GetSetMethodByReflection(fieldInfo);
19351963
#else
1964+
// if it's a struct, we want to use reflection, as linq expressions modify copies of the object and not the real thing
1965+
if (fieldInfo.DeclaringType.IsValueType)
1966+
return GetSetMethodByReflection(fieldInfo);
19361967
return GetSetMethodByExpression(fieldInfo);
19371968
#endif
19381969
}
@@ -2119,6 +2150,107 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
21192150
}
21202151

21212152
}
2153+
2154+
}
2155+
2156+
namespace GitHub.Unity
2157+
{
2158+
[System.AttributeUsage(System.AttributeTargets.Property |
2159+
System.AttributeTargets.Field)]
2160+
public sealed class NotSerializedAttribute : Attribute
2161+
{
2162+
}
2163+
2164+
public static class JsonSerializerExtensions
2165+
{
2166+
static JsonSerializationStrategy publicLowerCaseStrategy = new JsonSerializationStrategy(true, true);
2167+
static JsonSerializationStrategy publicUpperCaseStrategy = new JsonSerializationStrategy(false, true);
2168+
static JsonSerializationStrategy privateLowerCaseStrategy = new JsonSerializationStrategy(true, false);
2169+
static JsonSerializationStrategy privateUpperCaseStrategy = new JsonSerializationStrategy(false, false);
2170+
public static string ToJson<T>(this T model, bool lowerCase = false, bool onlyPublic = true)
2171+
{
2172+
return SimpleJson.SerializeObject(model, GetStrategy(lowerCase, onlyPublic));
2173+
}
2174+
2175+
public static T FromJson<T>(this string json, bool lowerCase = false, bool onlyPublic = true)
2176+
{
2177+
return SimpleJson.DeserializeObject<T>(json, GetStrategy(lowerCase, onlyPublic));
2178+
}
2179+
2180+
public static T FromObject<T>(this object obj, bool lowerCase = false, bool onlyPublic = true)
2181+
{
2182+
if (obj == null)
2183+
return default(T);
2184+
var ret = GetStrategy(lowerCase, onlyPublic).DeserializeObject(obj, typeof(T));
2185+
if (ret is T)
2186+
return (T)ret;
2187+
return default(T);
2188+
}
2189+
2190+
private static JsonSerializationStrategy GetStrategy(bool lowerCase, bool onlyPublic)
2191+
{
2192+
if (lowerCase && onlyPublic)
2193+
return publicLowerCaseStrategy;
2194+
if (lowerCase && !onlyPublic)
2195+
return privateLowerCaseStrategy;
2196+
if (!lowerCase && onlyPublic)
2197+
return publicUpperCaseStrategy;
2198+
return privateUpperCaseStrategy;
2199+
}
2200+
2201+
/// <summary>
2202+
/// Convert from PascalCase to camelCase.
2203+
/// </summary>
2204+
private static string ToJsonPropertyName(string propertyName)
2205+
{
2206+
Guard.ArgumentNotNullOrWhiteSpace(propertyName, "propertyName");
2207+
int i = 0;
2208+
while (i < propertyName.Length && char.IsUpper(propertyName[i]))
2209+
i++;
2210+
return propertyName.Substring(0, i).ToLowerInvariant() + propertyName.Substring(i);
2211+
}
2212+
2213+
class JsonSerializationStrategy : PocoJsonSerializerStrategy
2214+
{
2215+
private bool toLowerCase = false;
2216+
private bool onlyPublic = true;
2217+
2218+
public JsonSerializationStrategy(bool toLowerCase, bool onlyPublic)
2219+
{
2220+
this.toLowerCase = toLowerCase;
2221+
this.onlyPublic = onlyPublic;
2222+
}
2223+
2224+
protected override bool CanAddField(FieldInfo field)
2225+
{
2226+
var canAdd = base.CanAddField(field);
2227+
return canAdd && ((onlyPublic && field.IsPublic) || !onlyPublic);
2228+
}
2229+
2230+
protected override bool CanAddProperty(PropertyInfo property, MethodInfo method)
2231+
{
2232+
var canAdd = base.CanAddProperty(property, method);
2233+
if (!canAdd)
2234+
return false;
2235+
2236+
// we always serialize public things
2237+
if (method.IsPublic)
2238+
return true;
2239+
2240+
// if the getter is private and we're only serializing public things, skip this property
2241+
if (onlyPublic && method.Name.StartsWith("get_"))
2242+
return false;
2243+
2244+
return true;
2245+
}
2246+
2247+
protected override string MapClrMemberNameToJsonFieldName(string clrPropertyName)
2248+
{
2249+
if (!toLowerCase)
2250+
return base.MapClrMemberNameToJsonFieldName(clrPropertyName);
2251+
return ToJsonPropertyName(clrPropertyName);
2252+
}
2253+
}
21222254
}
21232255
}
21242256
// ReSharper restore LoopCanBeConvertedToQuery

src/GitHub.Api/Metrics/UsageTracker.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ private UsageStore LoadUsage()
4747
json = storePath.ReadAllText(Encoding.UTF8);
4848
if (json != null)
4949
{
50-
result = SimpleJson.DeserializeObject<UsageStore>(json);
50+
result = json.FromJson<UsageStore>();
5151
}
5252
}
5353
catch (Exception ex)
@@ -83,7 +83,7 @@ private void SaveUsage(UsageStore store)
8383

8484
try
8585
{
86-
var json = SimpleJson.SerializeObject(store);
86+
var json = store.ToJson();
8787
storePath.WriteAllText(json, Encoding.UTF8);
8888
}
8989
catch (Exception ex)

src/GitHub.Api/Platform/Settings.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ private void LoadFromCache(string path)
125125

126126
try
127127
{
128-
cacheData = SimpleJson.DeserializeObject<CacheData>(data);
128+
cacheData = data.FromJson<CacheData>();
129129
}
130130
catch(Exception ex)
131131
{
@@ -149,7 +149,7 @@ private bool SaveToCache(string path)
149149

150150
try
151151
{
152-
var data = SimpleJson.SerializeObject(cacheData);
152+
var data = cacheData.ToJson();
153153
writeAllText(path, data);
154154
}
155155
catch (Exception ex)

src/GitHub.Logging/LogHelper.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,13 @@ public static ILogging Instance
4545
set { instance = value; }
4646
}
4747

48+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
4849
public static ILogging GetLogger<T>()
4950
{
5051
return GetLogger(typeof(T));
5152
}
5253

54+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
5355
public static ILogging GetLogger(Type type)
5456
{
5557
return GetLogger(type.Name);

0 commit comments

Comments
 (0)