36
36
37
37
// NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke().
38
38
// 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
40
40
41
41
// NOTE: uncomment the following line if you are compiling under Window Metro style application/library.
42
42
// usually already defined in properties
66
66
using System . Reflection ;
67
67
using System . Runtime . Serialization ;
68
68
using System . Text ;
69
- using GitHub . Reflection ;
69
+ using System . Diagnostics ;
70
+ using System . Runtime . CompilerServices ;
71
+ using GitHub . Unity . Json ;
70
72
71
73
// ReSharper disable LoopCanBeConvertedToQuery
72
74
// ReSharper disable RedundantExplicitArrayCreation
73
75
// ReSharper disable SuggestUseVarKeywordEvident
74
- namespace GitHub
76
+ namespace GitHub . Unity . Json
75
77
{
76
78
/// <summary>
77
79
/// Represents the json array.
@@ -482,10 +484,7 @@ public override IEnumerable<string> GetDynamicMemberNames()
482
484
}
483
485
#endif
484
486
}
485
- }
486
487
487
- namespace GitHub
488
- {
489
488
/// <summary>
490
489
/// This class encodes and decodes JSON strings.
491
490
/// Spec. details, see http://www.json.org/
@@ -517,6 +516,7 @@ static class SimpleJson
517
516
518
517
private static readonly char [ ] EscapeTable ;
519
518
private static readonly char [ ] EscapeCharacters = new char [ ] { '"' , '\\ ' , '\b ' , '\f ' , '\n ' , '\r ' , '\t ' } ;
519
+ private static readonly string EscapeCharactersString = new string ( EscapeCharacters ) ;
520
520
521
521
static SimpleJson ( )
522
522
{
@@ -620,7 +620,7 @@ public static string EscapeToJavascriptString(string jsonString)
620
620
StringBuilder sb = new StringBuilder ( ) ;
621
621
char c ;
622
622
623
- for ( int i = 0 ; i < jsonString . Length ; )
623
+ for ( int i = 0 ; i < jsonString . Length ; )
624
624
{
625
625
c = jsonString [ i ++ ] ;
626
626
@@ -1282,14 +1282,14 @@ internal virtual ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(T
1282
1282
if ( propertyInfo . CanRead )
1283
1283
{
1284
1284
MethodInfo getMethod = ReflectionUtils . GetGetterMethodInfo ( propertyInfo ) ;
1285
- if ( getMethod . IsStatic || ! getMethod . IsPublic )
1285
+ if ( ! CanAddProperty ( propertyInfo , getMethod ) )
1286
1286
continue ;
1287
1287
result [ MapClrMemberNameToJsonFieldName ( propertyInfo . Name ) ] = ReflectionUtils . GetGetMethod ( propertyInfo ) ;
1288
1288
}
1289
1289
}
1290
1290
foreach ( FieldInfo fieldInfo in ReflectionUtils . GetFields ( type ) )
1291
1291
{
1292
- if ( fieldInfo . IsStatic || ! fieldInfo . IsPublic )
1292
+ if ( ! CanAddField ( fieldInfo ) )
1293
1293
continue ;
1294
1294
result [ MapClrMemberNameToJsonFieldName ( fieldInfo . Name ) ] = ReflectionUtils . GetGetMethod ( fieldInfo ) ;
1295
1295
}
@@ -1304,20 +1304,40 @@ internal virtual ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(T
1304
1304
if ( propertyInfo . CanWrite )
1305
1305
{
1306
1306
MethodInfo setMethod = ReflectionUtils . GetSetterMethodInfo ( propertyInfo ) ;
1307
- if ( setMethod . IsStatic || ! setMethod . IsPublic )
1307
+ if ( ! CanAddProperty ( propertyInfo , setMethod ) )
1308
1308
continue ;
1309
1309
result [ MapClrMemberNameToJsonFieldName ( propertyInfo . Name ) ] = new KeyValuePair < Type , ReflectionUtils . SetDelegate > ( propertyInfo . PropertyType , ReflectionUtils . GetSetMethod ( propertyInfo ) ) ;
1310
1310
}
1311
1311
}
1312
1312
foreach ( FieldInfo fieldInfo in ReflectionUtils . GetFields ( type ) )
1313
1313
{
1314
- if ( fieldInfo . IsInitOnly || fieldInfo . IsStatic || ! fieldInfo . IsPublic )
1314
+ if ( fieldInfo . IsInitOnly || ! CanAddField ( fieldInfo ) )
1315
1315
continue ;
1316
1316
result [ MapClrMemberNameToJsonFieldName ( fieldInfo . Name ) ] = new KeyValuePair < Type , ReflectionUtils . SetDelegate > ( fieldInfo . FieldType , ReflectionUtils . GetSetMethod ( fieldInfo ) ) ;
1317
1317
}
1318
1318
return result ;
1319
1319
}
1320
1320
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
+
1321
1341
public virtual bool TrySerializeNonPrimitiveObject ( object input , out object output )
1322
1342
{
1323
1343
return TrySerializeKnownTypes ( input , out output ) || TrySerializeUnknownTypes ( input , out output ) ;
@@ -1329,7 +1349,7 @@ public virtual object DeserializeObject(object value, Type type)
1329
1349
if ( type == null ) throw new ArgumentNullException ( "type" ) ;
1330
1350
string str = value as string ;
1331
1351
1332
- if ( type == typeof ( Guid ) && string . IsNullOrEmpty ( str ) )
1352
+ if ( type == typeof ( Guid ) && string . IsNullOrEmpty ( str ) )
1333
1353
return default ( Guid ) ;
1334
1354
1335
1355
if ( value == null )
@@ -1349,19 +1369,19 @@ public virtual object DeserializeObject(object value, Type type)
1349
1369
return new Guid ( str ) ;
1350
1370
if ( type == typeof ( Uri ) )
1351
1371
{
1352
- bool isValid = Uri . IsWellFormedUriString ( str , UriKind . RelativeOrAbsolute ) ;
1372
+ bool isValid = Uri . IsWellFormedUriString ( str , UriKind . RelativeOrAbsolute ) ;
1353
1373
1354
1374
Uri result ;
1355
1375
if ( isValid && Uri . TryCreate ( str , UriKind . RelativeOrAbsolute , out result ) )
1356
1376
return result ;
1357
1377
1358
- return null ;
1378
+ return null ;
1359
1379
}
1360
1380
1361
- if ( type == typeof ( string ) )
1362
- return str ;
1381
+ if ( type == typeof ( string ) )
1382
+ return str ;
1363
1383
1364
- return Convert . ChangeType ( str , type , CultureInfo . InvariantCulture ) ;
1384
+ return Convert . ChangeType ( str , type , CultureInfo . InvariantCulture ) ;
1365
1385
}
1366
1386
else
1367
1387
{
@@ -1592,8 +1612,6 @@ private static bool CanAdd(MemberInfo info, out string jsonKey)
1592
1612
1593
1613
#endif
1594
1614
1595
- namespace Reflection
1596
- {
1597
1615
// This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules
1598
1616
// that might be in place in the target project.
1599
1617
[ GeneratedCode ( "reflection-utils" , "1.0.0" ) ]
@@ -1648,7 +1666,7 @@ public static Type GetGenericListElementType(Type type)
1648
1666
foreach ( Type implementedInterface in interfaces )
1649
1667
{
1650
1668
if ( IsTypeGeneric ( implementedInterface ) &&
1651
- implementedInterface . GetGenericTypeDefinition ( ) == typeof ( IList < > ) )
1669
+ implementedInterface . GetGenericTypeDefinition ( ) == typeof ( IList < > ) )
1652
1670
{
1653
1671
return GetGenericTypeArguments ( implementedInterface ) [ 0 ] ;
1654
1672
}
@@ -1837,7 +1855,13 @@ public static ConstructorDelegate GetConstructorByReflection(ConstructorInfo con
1837
1855
public static ConstructorDelegate GetConstructorByReflection ( Type type , params Type [ ] argsType )
1838
1856
{
1839
1857
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 ) ; } ;
1841
1865
}
1842
1866
1843
1867
#if ! SIMPLE_JSON_NO_LINQ_EXPRESSION
@@ -1864,7 +1888,8 @@ public static ConstructorDelegate GetConstructorByExpression(ConstructorInfo con
1864
1888
public static ConstructorDelegate GetConstructorByExpression ( Type type , params Type [ ] argsType )
1865
1889
{
1866
1890
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 ) ;
1868
1893
}
1869
1894
1870
1895
#endif
@@ -1924,6 +1949,9 @@ public static SetDelegate GetSetMethod(PropertyInfo propertyInfo)
1924
1949
#if SIMPLE_JSON_NO_LINQ_EXPRESSION
1925
1950
return GetSetMethodByReflection ( propertyInfo ) ;
1926
1951
#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 ) ;
1927
1955
return GetSetMethodByExpression ( propertyInfo ) ;
1928
1956
#endif
1929
1957
}
@@ -1933,6 +1961,9 @@ public static SetDelegate GetSetMethod(FieldInfo fieldInfo)
1933
1961
#if SIMPLE_JSON_NO_LINQ_EXPRESSION
1934
1962
return GetSetMethodByReflection ( fieldInfo ) ;
1935
1963
#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 ) ;
1936
1967
return GetSetMethodByExpression ( fieldInfo ) ;
1937
1968
#endif
1938
1969
}
@@ -2119,6 +2150,107 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
2119
2150
}
2120
2151
2121
2152
}
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
+ }
2122
2254
}
2123
2255
}
2124
2256
// ReSharper restore LoopCanBeConvertedToQuery
0 commit comments