17
17
// <website>https://github.com/facebook-csharp-sdk/simple-json</website>
18
18
//-----------------------------------------------------------------------
19
19
20
- // VERSION: 0.26 .0
20
+ // VERSION: 0.38 .0
21
21
22
22
// NOTE: uncomment the following line to make SimpleJson class internal.
23
23
//#define SIMPLE_JSON_INTERNAL
31
31
// NOTE: uncomment the following line to enable DataContract support.
32
32
//#define SIMPLE_JSON_DATACONTRACT
33
33
34
+ // NOTE: uncomment the following line to enable IReadOnlyCollection<T> and IReadOnlyList<T> support.
35
+ //#define SIMPLE_JSON_READONLY_COLLECTIONS
36
+
34
37
// NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke().
35
38
// define if you are using .net framework <= 3.0 or < WP7.5
36
39
//#define SIMPLE_JSON_NO_LINQ_EXPRESSION
@@ -257,7 +260,7 @@ public void Clear()
257
260
/// </summary>
258
261
/// <param name="item">The item.</param>
259
262
/// <returns>
260
- /// <c>true</c> if [contains] [the specified item]; otherwise, <c>false</c>.
263
+ /// <c>true</c> if [contains] [the specified item]; otherwise, <c>false</c>.
261
264
/// </returns>
262
265
public bool Contains ( KeyValuePair < string , object > item )
263
266
{
@@ -294,7 +297,7 @@ public int Count
294
297
/// Gets a value indicating whether this instance is read only.
295
298
/// </summary>
296
299
/// <value>
297
- /// <c>true</c> if this instance is read only; otherwise, <c>false</c>.
300
+ /// <c>true</c> if this instance is read only; otherwise, <c>false</c>.
298
301
/// </value>
299
302
public bool IsReadOnly
300
303
{
@@ -512,6 +515,22 @@ static class SimpleJson
512
515
private const int TOKEN_NULL = 11 ;
513
516
private const int BUILDER_CAPACITY = 2000 ;
514
517
518
+ private static readonly char [ ] EscapeTable ;
519
+ private static readonly char [ ] EscapeCharacters = new char [ ] { '"' , '\\ ' , '\b ' , '\f ' , '\n ' , '\r ' , '\t ' } ;
520
+ private static readonly string EscapeCharactersString = new string ( EscapeCharacters ) ;
521
+
522
+ static SimpleJson ( )
523
+ {
524
+ EscapeTable = new char [ 93 ] ;
525
+ EscapeTable [ '"' ] = '"' ;
526
+ EscapeTable [ '\\ ' ] = '\\ ' ;
527
+ EscapeTable [ '\b ' ] = 'b' ;
528
+ EscapeTable [ '\f ' ] = 'f' ;
529
+ EscapeTable [ '\n ' ] = 'n' ;
530
+ EscapeTable [ '\r ' ] = 'r' ;
531
+ EscapeTable [ '\t ' ] = 't' ;
532
+ }
533
+
515
534
/// <summary>
516
535
/// Parses the string json into a value
517
536
/// </summary>
@@ -1073,29 +1092,50 @@ static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnum
1073
1092
1074
1093
static bool SerializeString ( string aString , StringBuilder builder )
1075
1094
{
1076
- builder . Append ( "\" " ) ;
1095
+ // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged)
1096
+ if ( aString . IndexOfAny ( EscapeCharacters ) == - 1 )
1097
+ {
1098
+ builder . Append ( '"' ) ;
1099
+ builder . Append ( aString ) ;
1100
+ builder . Append ( '"' ) ;
1101
+
1102
+ return true ;
1103
+ }
1104
+
1105
+ builder . Append ( '"' ) ;
1106
+ int safeCharacterCount = 0 ;
1077
1107
char [ ] charArray = aString . ToCharArray ( ) ;
1108
+
1078
1109
for ( int i = 0 ; i < charArray . Length ; i ++ )
1079
1110
{
1080
1111
char c = charArray [ i ] ;
1081
- if ( c == '"' )
1082
- builder . Append ( "\\ \" " ) ;
1083
- else if ( c == '\\ ' )
1084
- builder . Append ( "\\ \\ " ) ;
1085
- else if ( c == '\b ' )
1086
- builder . Append ( "\\ b" ) ;
1087
- else if ( c == '\f ' )
1088
- builder . Append ( "\\ f" ) ;
1089
- else if ( c == '\n ' )
1090
- builder . Append ( "\\ n" ) ;
1091
- else if ( c == '\r ' )
1092
- builder . Append ( "\\ r" ) ;
1093
- else if ( c == '\t ' )
1094
- builder . Append ( "\\ t" ) ;
1112
+
1113
+ // Non ascii characters are fine, buffer them up and send them to the builder
1114
+ // in larger chunks if possible. The escape table is a 1:1 translation table
1115
+ // with \0 [default(char)] denoting a safe character.
1116
+ if ( c >= EscapeTable . Length || EscapeTable [ c ] == default ( char ) )
1117
+ {
1118
+ safeCharacterCount ++ ;
1119
+ }
1095
1120
else
1096
- builder . Append ( c ) ;
1121
+ {
1122
+ if ( safeCharacterCount > 0 )
1123
+ {
1124
+ builder . Append ( charArray , i - safeCharacterCount , safeCharacterCount ) ;
1125
+ safeCharacterCount = 0 ;
1126
+ }
1127
+
1128
+ builder . Append ( '\\ ' ) ;
1129
+ builder . Append ( EscapeTable [ c ] ) ;
1130
+ }
1097
1131
}
1098
- builder . Append ( "\" " ) ;
1132
+
1133
+ if ( safeCharacterCount > 0 )
1134
+ {
1135
+ builder . Append ( charArray , charArray . Length - safeCharacterCount , safeCharacterCount ) ;
1136
+ }
1137
+
1138
+ builder . Append ( '"' ) ;
1099
1139
return true ;
1100
1140
}
1101
1141
@@ -1308,7 +1348,21 @@ public virtual object DeserializeObject(object value, Type type)
1308
1348
return DateTimeOffset . ParseExact ( str , Iso8601Format , CultureInfo . InvariantCulture , DateTimeStyles . AssumeUniversal | DateTimeStyles . AdjustToUniversal ) ;
1309
1349
if ( type == typeof ( Guid ) || ( ReflectionUtils . IsNullableType ( type ) && Nullable . GetUnderlyingType ( type ) == typeof ( Guid ) ) )
1310
1350
return new Guid ( str ) ;
1311
- return str ;
1351
+ if ( type == typeof ( Uri ) )
1352
+ {
1353
+ bool isValid = Uri . IsWellFormedUriString ( str , UriKind . RelativeOrAbsolute ) ;
1354
+
1355
+ Uri result ;
1356
+ if ( isValid && Uri . TryCreate ( str , UriKind . RelativeOrAbsolute , out result ) )
1357
+ return result ;
1358
+
1359
+ return null ;
1360
+ }
1361
+
1362
+ if ( type == typeof ( string ) )
1363
+ return str ;
1364
+
1365
+ return Convert . ChangeType ( str , type , CultureInfo . InvariantCulture ) ;
1312
1366
}
1313
1367
else
1314
1368
{
@@ -1395,9 +1449,8 @@ public virtual object DeserializeObject(object value, Type type)
1395
1449
}
1396
1450
else if ( ReflectionUtils . IsTypeGenericeCollectionInterface ( type ) || ReflectionUtils . IsAssignableFrom ( typeof ( IList ) , type ) )
1397
1451
{
1398
- Type innerType = ReflectionUtils . GetGenericTypeArguments ( type ) [ 0 ] ;
1399
- Type genericType = typeof ( List < > ) . MakeGenericType ( innerType ) ;
1400
- list = ( IList ) ConstructorCache [ genericType ] ( jsonObject . Count ) ;
1452
+ Type innerType = ReflectionUtils . GetGenericListElementType ( type ) ;
1453
+ list = ( IList ) ( ConstructorCache [ type ] ?? ConstructorCache [ typeof ( List < > ) . MakeGenericType ( innerType ) ] ) ( jsonObject . Count ) ;
1401
1454
foreach ( object o in jsonObject )
1402
1455
list . Add ( DeserializeObject ( o , innerType ) ) ;
1403
1456
}
@@ -1543,7 +1596,7 @@ private static bool CanAdd(MemberInfo info, out string jsonKey)
1543
1596
namespace Reflection
1544
1597
{
1545
1598
// This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules
1546
- // that might be in place in the target project.
1599
+ // that might be in place in the target project.
1547
1600
[ GeneratedCode ( "reflection-utils" , "1.0.0" ) ]
1548
1601
#if SIMPLE_JSON_REFLECTION_UTILS_PUBLIC
1549
1602
public
@@ -1560,6 +1613,18 @@ class ReflectionUtils
1560
1613
1561
1614
public delegate TValue ThreadSafeDictionaryValueFactory < TKey , TValue > ( TKey key ) ;
1562
1615
1616
+ #if SIMPLE_JSON_TYPEINFO
1617
+ public static TypeInfo GetTypeInfo ( Type type )
1618
+ {
1619
+ return type . GetTypeInfo ( ) ;
1620
+ }
1621
+ #else
1622
+ public static Type GetTypeInfo ( Type type )
1623
+ {
1624
+ return type ;
1625
+ }
1626
+ #endif
1627
+
1563
1628
public static Attribute GetAttribute ( MemberInfo info , Type type )
1564
1629
{
1565
1630
#if SIMPLE_JSON_TYPEINFO
@@ -1573,6 +1638,25 @@ public static Attribute GetAttribute(MemberInfo info, Type type)
1573
1638
#endif
1574
1639
}
1575
1640
1641
+ public static Type GetGenericListElementType ( Type type )
1642
+ {
1643
+ IEnumerable < Type > interfaces ;
1644
+ #if SIMPLE_JSON_TYPEINFO
1645
+ interfaces = type . GetTypeInfo ( ) . ImplementedInterfaces ;
1646
+ #else
1647
+ interfaces = type . GetInterfaces ( ) ;
1648
+ #endif
1649
+ foreach ( Type implementedInterface in interfaces )
1650
+ {
1651
+ if ( IsTypeGeneric ( implementedInterface ) &&
1652
+ implementedInterface . GetGenericTypeDefinition ( ) == typeof ( IList < > ) )
1653
+ {
1654
+ return GetGenericTypeArguments ( implementedInterface ) [ 0 ] ;
1655
+ }
1656
+ }
1657
+ return GetGenericTypeArguments ( type ) [ 0 ] ;
1658
+ }
1659
+
1576
1660
public static Attribute GetAttribute ( Type objectType , Type attributeType )
1577
1661
{
1578
1662
@@ -1596,57 +1680,52 @@ public static Type[] GetGenericTypeArguments(Type type)
1596
1680
#endif
1597
1681
}
1598
1682
1683
+ public static bool IsTypeGeneric ( Type type )
1684
+ {
1685
+ return GetTypeInfo ( type ) . IsGenericType ;
1686
+ }
1687
+
1599
1688
public static bool IsTypeGenericeCollectionInterface ( Type type )
1600
1689
{
1601
- #if SIMPLE_JSON_TYPEINFO
1602
- if ( ! type . GetTypeInfo ( ) . IsGenericType )
1603
- #else
1604
- if ( ! type . IsGenericType )
1605
- #endif
1690
+ if ( ! IsTypeGeneric ( type ) )
1606
1691
return false ;
1607
1692
1608
1693
Type genericDefinition = type . GetGenericTypeDefinition ( ) ;
1609
1694
1610
- return ( genericDefinition == typeof ( IList < > ) || genericDefinition == typeof ( ICollection < > ) || genericDefinition == typeof ( IEnumerable < > ) ) ;
1695
+ return ( genericDefinition == typeof ( IList < > )
1696
+ || genericDefinition == typeof ( ICollection < > )
1697
+ || genericDefinition == typeof ( IEnumerable < > )
1698
+ #if SIMPLE_JSON_READONLY_COLLECTIONS
1699
+ || genericDefinition == typeof ( IReadOnlyCollection < > )
1700
+ || genericDefinition == typeof ( IReadOnlyList < > )
1701
+ #endif
1702
+ ) ;
1611
1703
}
1612
1704
1613
1705
public static bool IsAssignableFrom ( Type type1 , Type type2 )
1614
1706
{
1615
- #if SIMPLE_JSON_TYPEINFO
1616
- return type1 . GetTypeInfo ( ) . IsAssignableFrom ( type2 . GetTypeInfo ( ) ) ;
1617
- #else
1618
- return type1 . IsAssignableFrom ( type2 ) ;
1619
- #endif
1707
+ return GetTypeInfo ( type1 ) . IsAssignableFrom ( GetTypeInfo ( type2 ) ) ;
1620
1708
}
1621
1709
1622
1710
public static bool IsTypeDictionary ( Type type )
1623
1711
{
1624
1712
#if SIMPLE_JSON_TYPEINFO
1625
1713
if ( typeof ( IDictionary < , > ) . GetTypeInfo ( ) . IsAssignableFrom ( type . GetTypeInfo ( ) ) )
1626
1714
return true ;
1627
-
1628
- if ( ! type . GetTypeInfo ( ) . IsGenericType )
1629
- return false ;
1630
1715
#else
1631
1716
if ( typeof ( System . Collections . IDictionary ) . IsAssignableFrom ( type ) )
1632
1717
return true ;
1633
-
1634
- if ( ! type . IsGenericType )
1635
- return false ;
1636
1718
#endif
1719
+ if ( ! GetTypeInfo ( type ) . IsGenericType )
1720
+ return false ;
1721
+
1637
1722
Type genericDefinition = type . GetGenericTypeDefinition ( ) ;
1638
1723
return genericDefinition == typeof ( IDictionary < , > ) ;
1639
1724
}
1640
1725
1641
1726
public static bool IsNullableType ( Type type )
1642
1727
{
1643
- return
1644
- #if SIMPLE_JSON_TYPEINFO
1645
- type . GetTypeInfo ( ) . IsGenericType
1646
- #else
1647
- type . IsGenericType
1648
- #endif
1649
- && type . GetGenericTypeDefinition ( ) == typeof ( Nullable < > ) ;
1728
+ return GetTypeInfo ( type ) . IsGenericType && type . GetGenericTypeDefinition ( ) == typeof ( Nullable < > ) ;
1650
1729
}
1651
1730
1652
1731
public static object ToNullableType ( object obj , Type nullableType )
@@ -1656,11 +1735,7 @@ public static object ToNullableType(object obj, Type nullableType)
1656
1735
1657
1736
public static bool IsValueType ( Type type )
1658
1737
{
1659
- #if SIMPLE_JSON_TYPEINFO
1660
- return type . GetTypeInfo ( ) . IsValueType ;
1661
- #else
1662
- return type . IsValueType ;
1663
- #endif
1738
+ return GetTypeInfo ( type ) . IsValueType ;
1664
1739
}
1665
1740
1666
1741
public static IEnumerable < ConstructorInfo > GetConstructors ( Type type )
@@ -1704,7 +1779,7 @@ public static ConstructorInfo GetConstructorInfo(Type type, params Type[] argsTy
1704
1779
public static IEnumerable < PropertyInfo > GetProperties ( Type type )
1705
1780
{
1706
1781
#if SIMPLE_JSON_TYPEINFO
1707
- return type . GetTypeInfo ( ) . DeclaredProperties ;
1782
+ return type . GetRuntimeProperties ( ) ;
1708
1783
#else
1709
1784
return type . GetProperties ( BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Static ) ;
1710
1785
#endif
@@ -1713,7 +1788,7 @@ public static IEnumerable<PropertyInfo> GetProperties(Type type)
1713
1788
public static IEnumerable < FieldInfo > GetFields ( Type type )
1714
1789
{
1715
1790
#if SIMPLE_JSON_TYPEINFO
1716
- return type . GetTypeInfo ( ) . DeclaredFields ;
1791
+ return type . GetRuntimeFields ( ) ;
1717
1792
#else
1718
1793
return type . GetFields ( BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Static ) ;
1719
1794
#endif
0 commit comments