18
18
using System . Globalization ;
19
19
using System . IO ;
20
20
using System . Text . RegularExpressions ;
21
+ using MongoDB . Shared ;
21
22
22
23
namespace MongoDB . Bson . IO
23
24
{
@@ -923,6 +924,14 @@ private BsonReaderState GetNextState()
923
924
}
924
925
}
925
926
927
+ private bool IsValidBinaryDataSubTypeString ( string value )
928
+ {
929
+ return
930
+ value . Length >= 1 &&
931
+ value . Length <= 2 &&
932
+ HexUtils . IsValidHexString ( value ) ;
933
+ }
934
+
926
935
private BsonValue ParseBinDataConstructor ( )
927
936
{
928
937
VerifyToken ( "(" ) ;
@@ -956,19 +965,117 @@ private BsonValue ParseBinDataExtendedJson()
956
965
{
957
966
VerifyToken ( ":" ) ;
958
967
959
- var bytesToken = PopToken ( ) ;
960
- if ( bytesToken . Type != JsonTokenType . String )
968
+ byte [ ] bytes ;
969
+ BsonBinarySubType subType ;
970
+
971
+ var nextToken = PopToken ( ) ;
972
+ if ( nextToken . Type == JsonTokenType . BeginObject )
961
973
{
962
- var message = string . Format ( "JSON reader expected a string but found '{0}'." , bytesToken . Lexeme ) ;
974
+ ParseBinDataExtendedJsonCanonical ( out bytes , out subType ) ;
975
+ }
976
+ else
977
+ {
978
+ ParseBinDataExtendedJsonLegacy ( nextToken , out bytes , out subType ) ;
979
+ }
980
+
981
+ VerifyToken ( "}" ) ;
982
+
983
+ GuidRepresentation guidRepresentation ;
984
+ switch ( subType )
985
+ {
986
+ case BsonBinarySubType . UuidLegacy : guidRepresentation = _jsonReaderSettings . GuidRepresentation ; break ;
987
+ case BsonBinarySubType . UuidStandard : guidRepresentation = GuidRepresentation . Standard ; break ;
988
+ default : guidRepresentation = GuidRepresentation . Unspecified ; break ;
989
+ }
990
+
991
+ return new BsonBinaryData ( bytes , subType , guidRepresentation ) ;
992
+ }
993
+
994
+ private void ParseBinDataExtendedJsonCanonical ( out byte [ ] bytes , out BsonBinarySubType subType )
995
+ {
996
+ string base64String = null ;
997
+ string subTypeString = null ;
998
+
999
+ var nextToken = PopToken ( ) ;
1000
+ while ( nextToken . Type != JsonTokenType . EndObject )
1001
+ {
1002
+ if ( nextToken . Type != JsonTokenType . String && nextToken . Type != JsonTokenType . UnquotedString )
1003
+ {
1004
+ var message = string . Format ( "JSON reader expected a string but found '{0}'." , nextToken . Lexeme ) ;
1005
+ throw new FormatException ( message ) ;
1006
+ }
1007
+ var name = nextToken . StringValue ;
1008
+
1009
+ nextToken = PopToken ( ) ;
1010
+ if ( nextToken . Type != JsonTokenType . Colon )
1011
+ {
1012
+ var message = string . Format ( "JSON reader expected ':' but found '{0}'." , nextToken . Lexeme ) ;
1013
+ throw new FormatException ( message ) ;
1014
+ }
1015
+
1016
+ nextToken = PopToken ( ) ;
1017
+ if ( nextToken . Type != JsonTokenType . String )
1018
+ {
1019
+ var message = string . Format ( "JSON reader expected a string but found '{0}'." , nextToken . Lexeme ) ;
1020
+ throw new FormatException ( message ) ;
1021
+ }
1022
+ var value = nextToken . StringValue ;
1023
+
1024
+ switch ( name )
1025
+ {
1026
+ case "base64" : base64String = value ; break ;
1027
+ case "subType" : subTypeString = value ; break ;
1028
+ default :
1029
+ var message = string . Format ( "JSON reader expected 'base64' or 'subType', but found '{0}'." , name ) ;
1030
+ throw new FormatException ( message ) ;
1031
+ }
1032
+
1033
+ nextToken = PopToken ( ) ;
1034
+ if ( nextToken . Type != JsonTokenType . Comma && nextToken . Type != JsonTokenType . EndObject )
1035
+ {
1036
+ var message = string . Format ( "JSON reader expected ',' or '}}' but found '{0}'." , nextToken . Lexeme ) ;
1037
+ throw new FormatException ( message ) ;
1038
+ }
1039
+
1040
+ if ( nextToken . Type == JsonTokenType . Comma )
1041
+ {
1042
+ nextToken = PopToken ( ) ;
1043
+ }
1044
+ }
1045
+
1046
+ if ( base64String == null )
1047
+ {
1048
+ var message = "JSON reader expected $binary to contain a 'base64' element." ;
963
1049
throw new FormatException ( message ) ;
964
1050
}
965
- var bytes = Convert . FromBase64String ( bytesToken . StringValue ) ;
1051
+ if ( subTypeString == null )
1052
+ {
1053
+ var message = "JSON reader expected $binary to contain a 'subType' element." ;
1054
+ throw new FormatException ( message ) ;
1055
+ }
1056
+ if ( ! IsValidBinaryDataSubTypeString ( subTypeString ) )
1057
+ {
1058
+ var message = string . Format ( "JSON reader expected subType to be a one or two digit hex string, but found '{0}'." , subTypeString ) ;
1059
+ throw new FormatException ( message ) ;
1060
+ }
1061
+
1062
+ bytes = Convert . FromBase64String ( base64String ) ;
1063
+ subType = ( BsonBinarySubType ) HexUtils . ParseInt32 ( subTypeString ) ;
1064
+ }
1065
+
1066
+ private void ParseBinDataExtendedJsonLegacy ( JsonToken nextToken , out byte [ ] bytes , out BsonBinarySubType subType )
1067
+ {
1068
+ if ( nextToken . Type != JsonTokenType . String )
1069
+ {
1070
+ var message = string . Format ( "JSON reader expected a string but found '{0}'." , nextToken . Lexeme ) ;
1071
+ throw new FormatException ( message ) ;
1072
+ }
1073
+ bytes = Convert . FromBase64String ( nextToken . StringValue ) ;
966
1074
967
1075
VerifyToken ( "," ) ;
968
1076
VerifyString ( "$type" ) ;
969
1077
VerifyToken ( ":" ) ;
970
1078
971
- BsonBinarySubType subType ;
972
1079
var subTypeToken = PopToken ( ) ;
973
1080
if ( subTypeToken . Type == JsonTokenType . String )
974
1081
{
@@ -983,18 +1090,6 @@ private BsonValue ParseBinDataExtendedJson()
983
1090
var message = string . Format ( "JSON reader expected a string or integer but found '{0}'." , subTypeToken . Lexeme ) ;
984
1091
throw new FormatException ( message ) ;
985
1092
}
986
-
987
- VerifyToken ( "}" ) ;
988
-
989
- GuidRepresentation guidRepresentation ;
990
- switch ( subType )
991
- {
992
- case BsonBinarySubType . UuidLegacy : guidRepresentation = _jsonReaderSettings . GuidRepresentation ; break ;
993
- case BsonBinarySubType . UuidStandard : guidRepresentation = GuidRepresentation . Standard ; break ;
994
- default : guidRepresentation = GuidRepresentation . Unspecified ; break ;
995
- }
996
-
997
- return new BsonBinaryData ( bytes , subType , guidRepresentation ) ;
998
1093
}
999
1094
1000
1095
private BsonValue ParseHexDataConstructor ( )
@@ -1218,10 +1313,12 @@ private BsonType ParseExtendedJson()
1218
1313
case "$maxkey" : case "$maxKey" : _currentValue = ParseMaxKeyExtendedJson ( ) ; return BsonType . MaxKey ;
1219
1314
case "$minkey" : case "$minKey" : _currentValue = ParseMinKeyExtendedJson ( ) ; return BsonType . MinKey ;
1220
1315
case "$numberDecimal" : _currentValue = ParseNumberDecimalExtendedJson ( ) ; return BsonType . Decimal128 ;
1316
+ case "$numberDouble" : _currentValue = ParseNumberDoubleExtendedJson ( ) ; return BsonType . Double ;
1221
1317
case "$numberInt" : _currentValue = ParseNumberIntExtendedJson ( ) ; return BsonType . Int32 ;
1222
1318
case "$numberLong" : _currentValue = ParseNumberLongExtendedJson ( ) ; return BsonType . Int64 ;
1223
1319
case "$oid" : _currentValue = ParseObjectIdExtendedJson ( ) ; return BsonType . ObjectId ;
1224
- case "$regex" : _currentValue = ParseRegularExpressionExtendedJson ( ) ; return BsonType . RegularExpression ;
1320
+ case "$regex" : _currentValue = ParseRegularExpressionExtendedJsonLegacy ( ) ; return BsonType . RegularExpression ;
1321
+ case "$regularExpression" : _currentValue = ParseRegularExpressionExtendedJsonCanonical ( ) ; return BsonType . RegularExpression ;
1225
1322
case "$symbol" : _currentValue = ParseSymbolExtendedJson ( ) ; return BsonType . Symbol ;
1226
1323
case "$timestamp" : _currentValue = ParseTimestampExtendedJson ( ) ; return BsonType . Timestamp ;
1227
1324
case "$undefined" : _currentValue = ParseUndefinedExtendedJson ( ) ; return BsonType . Undefined ;
@@ -1549,6 +1646,31 @@ private BsonValue ParseNumberDecimalExtendedJson()
1549
1646
return ( BsonDecimal128 ) value ;
1550
1647
}
1551
1648
1649
+ private BsonValue ParseNumberDoubleExtendedJson ( )
1650
+ {
1651
+ VerifyToken ( ":" ) ;
1652
+
1653
+ double value ;
1654
+ var valueToken = PopToken ( ) ;
1655
+ if ( valueToken . IsNumber )
1656
+ {
1657
+ value = valueToken . DoubleValue ;
1658
+ }
1659
+ else if ( valueToken . Type == JsonTokenType . String )
1660
+ {
1661
+ value = JsonConvert . ToDouble ( valueToken . StringValue ) ;
1662
+ }
1663
+ else
1664
+ {
1665
+ var message = string . Format ( "JSON reader expected a number or numeric string but found '{0}'." , valueToken . Lexeme ) ;
1666
+ throw new FormatException ( message ) ;
1667
+ }
1668
+
1669
+ VerifyToken ( "}" ) ;
1670
+
1671
+ return ( BsonDouble ) value ;
1672
+ }
1673
+
1552
1674
private BsonValue ParseNumberIntExtendedJson ( )
1553
1675
{
1554
1676
VerifyToken ( ":" ) ;
@@ -1623,6 +1745,77 @@ private BsonValue ParseObjectIdExtendedJson()
1623
1745
return new BsonObjectId ( ObjectId . Parse ( valueToken . StringValue ) ) ;
1624
1746
}
1625
1747
1748
+ private BsonValue ParseRegularExpressionExtendedJsonCanonical ( )
1749
+ {
1750
+ VerifyToken ( ":" ) ;
1751
+ VerifyToken ( "{" ) ;
1752
+
1753
+ string pattern = null ;
1754
+ string options = null ;
1755
+
1756
+ var nextToken = PopToken ( ) ;
1757
+ while ( nextToken . Type != JsonTokenType . EndObject )
1758
+ {
1759
+ if ( nextToken . Type != JsonTokenType . String && nextToken . Type != JsonTokenType . UnquotedString )
1760
+ {
1761
+ var message = string . Format ( "JSON reader expected a string but found '{0}'." , nextToken . Lexeme ) ;
1762
+ throw new FormatException ( message ) ;
1763
+ }
1764
+ var name = nextToken . StringValue ;
1765
+
1766
+ VerifyToken ( ":" ) ;
1767
+
1768
+ nextToken = PopToken ( ) ;
1769
+ if ( nextToken . Type != JsonTokenType . String )
1770
+ {
1771
+ var message = string . Format ( "JSON reader expected a string but found '{0}'." , nextToken . Lexeme ) ;
1772
+ throw new FormatException ( message ) ;
1773
+ }
1774
+ var value = nextToken . StringValue ;
1775
+
1776
+ switch ( name )
1777
+ {
1778
+ case "pattern" :
1779
+ pattern = value ;
1780
+ break ;
1781
+ case "options" :
1782
+ options = value ;
1783
+ break ;
1784
+ default :
1785
+ var message = string . Format ( "JSON reader expected 'pattern' or 'options' but found '{0}'." , nextToken . Lexeme ) ;
1786
+ throw new FormatException ( message ) ;
1787
+ }
1788
+
1789
+ nextToken = PopToken ( ) ;
1790
+ if ( nextToken . Type != JsonTokenType . Comma && nextToken . Type != JsonTokenType . EndObject )
1791
+ {
1792
+ var message = string . Format ( "JSON reader expected ',' or '}}' but found '{0}'." , nextToken . Lexeme ) ;
1793
+ throw new FormatException ( message ) ;
1794
+ }
1795
+
1796
+ if ( nextToken . Type == JsonTokenType . Comma )
1797
+ {
1798
+ nextToken = PopToken ( ) ;
1799
+ }
1800
+ }
1801
+
1802
+ VerifyToken ( "}" ) ;
1803
+
1804
+ if ( pattern == null )
1805
+ {
1806
+ var message = "JSON reader expected $regularExpression to contain a 'pattern' element." ;
1807
+ throw new FormatException ( message ) ;
1808
+ }
1809
+
1810
+ if ( options == null )
1811
+ {
1812
+ var message = "JSON reader expected $regularExpression to contain an 'options' element." ;
1813
+ throw new FormatException ( message ) ;
1814
+ }
1815
+
1816
+ return new BsonRegularExpression ( pattern , options ) ;
1817
+ }
1818
+
1626
1819
private BsonValue ParseRegularExpressionConstructor ( )
1627
1820
{
1628
1821
VerifyToken ( "(" ) ;
@@ -1652,7 +1845,7 @@ private BsonValue ParseRegularExpressionConstructor()
1652
1845
return new BsonRegularExpression ( patternToken . StringValue , options ) ;
1653
1846
}
1654
1847
1655
- private BsonValue ParseRegularExpressionExtendedJson ( )
1848
+ private BsonValue ParseRegularExpressionExtendedJsonLegacy ( )
1656
1849
{
1657
1850
VerifyToken ( ":" ) ;
1658
1851
var patternToken = PopToken ( ) ;
@@ -1908,7 +2101,7 @@ private void VerifyString(string expectedString)
1908
2101
var token = PopToken ( ) ;
1909
2102
if ( ( token . Type != JsonTokenType . String && token . Type != JsonTokenType . UnquotedString ) || token . StringValue != expectedString )
1910
2103
{
1911
- var message = string . Format ( "JSON reader expected '{0}' but found '{1}'." , expectedString , token . StringValue ) ;
2104
+ var message = string . Format ( "JSON reader expected string '{0}' but found '{1}'." , expectedString , token . Lexeme ) ;
1912
2105
throw new FormatException ( message ) ;
1913
2106
}
1914
2107
}
0 commit comments