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

Commit 50cc842

Browse files
committed
Merge pull request #429 from scottmcarthur/NumberParsing
Number parsing
2 parents 7f7e67e + 6c46779 commit 50cc842

5 files changed

Lines changed: 119 additions & 44 deletions

File tree

src/ServiceStack.Text/Common/DeserializeType.cs

Lines changed: 57 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -149,50 +149,63 @@ public static object ParseQuotedPrimitive(string value)
149149
return Serializer.UnescapeString(value);
150150
}
151151

152-
public static object ParsePrimitive(string value)
153-
{
154-
if (string.IsNullOrEmpty(value)) return null;
155-
156-
bool boolValue;
157-
if (bool.TryParse(value, out boolValue)) return boolValue;
158-
float floatValue;
159-
double doubleValue;
160-
161-
decimal decimalValue;
162-
if (decimal.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out decimalValue))
163-
{
164-
if (!JsConfig.TryToParseNumericType)
165-
return decimalValue;
166-
167-
if (decimalValue == decimal.Truncate(decimalValue))
168-
{
169-
if (decimalValue <= byte.MaxValue && decimalValue >= byte.MinValue) return (byte)decimalValue;
170-
if (decimalValue <= sbyte.MaxValue && decimalValue >= sbyte.MinValue) return (sbyte)decimalValue;
171-
if (decimalValue <= Int16.MaxValue && decimalValue >= Int16.MinValue) return (Int16)decimalValue;
172-
if (decimalValue <= UInt16.MaxValue && decimalValue >= UInt16.MinValue) return (UInt16)decimalValue;
173-
if (decimalValue <= Int32.MaxValue && decimalValue >= Int32.MinValue) return (Int32)decimalValue;
174-
if (decimalValue <= UInt32.MaxValue && decimalValue >= UInt32.MinValue) return (UInt32)decimalValue;
175-
if (decimalValue <= Int64.MaxValue && decimalValue >= Int64.MinValue) return (Int64)decimalValue;
176-
if (decimalValue <= UInt64.MaxValue && decimalValue >= UInt64.MinValue) return (UInt64)decimalValue;
177-
}
178-
179-
if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out floatValue))
180-
return floatValue;
181-
182-
if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out doubleValue))
183-
return doubleValue;
184-
185-
return decimalValue;
186-
}
187-
188-
if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out floatValue))
189-
return floatValue;
190-
191-
if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out doubleValue))
192-
return doubleValue;
193-
194-
return null;
195-
}
152+
public static object ParsePrimitive(string value)
153+
{
154+
if (string.IsNullOrEmpty(value)) return null;
155+
156+
bool boolValue;
157+
if (bool.TryParse(value, out boolValue)) return boolValue;
158+
159+
// Parse as decimal
160+
decimal decimalValue;
161+
var acceptDecimal = JsConfig.ParsePrimitiveFloatingPointTypes.HasFlag(ParseAsType.Decimal);
162+
var hasDecimal = decimal.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out decimalValue);
163+
164+
// Check if the number is an Primitive Integer type given that we have a decimal
165+
if(hasDecimal && decimalValue == decimal.Truncate(decimalValue))
166+
{
167+
// Value is a whole number
168+
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.Byte) && decimalValue <= byte.MaxValue && decimalValue >= byte.MinValue) return (byte)decimalValue;
169+
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.SByte) && decimalValue <= sbyte.MaxValue && decimalValue >= sbyte.MinValue) return (sbyte)decimalValue;
170+
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.Int16) && decimalValue <= Int16.MaxValue && decimalValue >= Int16.MinValue) return (Int16)decimalValue;
171+
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.UInt16) && decimalValue <= UInt16.MaxValue && decimalValue >= UInt16.MinValue) return (UInt16)decimalValue;
172+
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.Int32) && decimalValue <= Int32.MaxValue && decimalValue >= Int32.MinValue) return (Int32)decimalValue;
173+
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.UInt32) && decimalValue <= UInt32.MaxValue && decimalValue >= UInt32.MinValue) return (UInt32)decimalValue;
174+
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.Int64) && decimalValue <= Int64.MaxValue && decimalValue >= Int64.MinValue) return (Int64)decimalValue;
175+
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.UInt64) && decimalValue <= UInt64.MaxValue && decimalValue >= UInt64.MinValue) return (UInt64)decimalValue;
176+
return null;
177+
}
178+
179+
// Value is a floating point number
180+
181+
// Return a decimal if the user accepts a decimal
182+
if(hasDecimal && acceptDecimal)
183+
return decimalValue;
184+
185+
// Parse as double if decimal failed or user wants a double
186+
double doubleValue = 0;
187+
var acceptDouble = JsConfig.ParsePrimitiveFloatingPointTypes.HasFlag(ParseAsType.Double);
188+
var hasDouble = (!hasDecimal || acceptDouble) && double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out doubleValue);
189+
190+
// Return a double if the user accepts a double
191+
if(acceptDouble && hasDouble)
192+
return doubleValue;
193+
194+
// Parse as float
195+
float floatValue;
196+
var acceptFloat = JsConfig.ParsePrimitiveFloatingPointTypes.HasFlag(ParseAsType.Single);
197+
var hasFloat = float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out floatValue);
198+
199+
// Return a float if the user accepts a float
200+
if(acceptFloat && hasFloat)
201+
return floatValue;
202+
203+
// Default to decimal, then double , then float or null
204+
if(hasDecimal) return decimalValue;
205+
if(hasDouble) return doubleValue;
206+
if(hasFloat) return floatValue;
207+
return null;
208+
}
196209

197210
internal static object ParsePrimitive(string value, char firstChar)
198211
{

src/ServiceStack.Text/JsConfig.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using System.Collections.Generic;
34
using System.IO;
45
using System.Reflection;
@@ -30,6 +31,8 @@ public static JsConfigScope With(
3031
bool? convertObjectTypesIntoStringDictionary = null,
3132
bool? tryToParsePrimitiveTypeValues = null,
3233
bool? tryToParseNumericType = null,
34+
ParseAsType? parsePrimitiveFloatingPointTypes = null,
35+
ParseAsType? parsePrimitiveIntegerTypes = null,
3336
bool? includeNullValues = null,
3437
bool? includeDefaultEnums = null,
3538
bool? excludeTypeInfo = null,
@@ -60,6 +63,10 @@ public static JsConfigScope With(
6063
ConvertObjectTypesIntoStringDictionary = convertObjectTypesIntoStringDictionary ?? sConvertObjectTypesIntoStringDictionary,
6164
TryToParsePrimitiveTypeValues = tryToParsePrimitiveTypeValues ?? sTryToParsePrimitiveTypeValues,
6265
TryToParseNumericType = tryToParseNumericType ?? sTryToParseNumericType,
66+
67+
ParsePrimitiveFloatingPointTypes = parsePrimitiveFloatingPointTypes ?? sParsePrimitiveFloatingPointTypes,
68+
ParsePrimitiveIntegerTypes = parsePrimitiveIntegerTypes ?? sParsePrimitiveIntegerTypes,
69+
6370
IncludeNullValues = includeNullValues ?? sIncludeNullValues,
6471
IncludeDefaultEnums = includeDefaultEnums ?? sIncludeDefaultEnums,
6572
ExcludeTypeInfo = excludeTypeInfo ?? sExcludeTypeInfo,
@@ -132,6 +139,36 @@ public static bool TryToParseNumericType
132139
}
133140
}
134141

142+
private static ParseAsType? sParsePrimitiveFloatingPointTypes;
143+
public static ParseAsType ParsePrimitiveFloatingPointTypes
144+
{
145+
get
146+
{
147+
return (JsConfigScope.Current != null ? JsConfigScope.Current.ParsePrimitiveFloatingPointTypes : null)
148+
?? sParsePrimitiveFloatingPointTypes
149+
?? ParseAsType.Decimal;
150+
}
151+
set
152+
{
153+
if (sParsePrimitiveFloatingPointTypes == null) sParsePrimitiveFloatingPointTypes = value;
154+
}
155+
}
156+
157+
private static ParseAsType? sParsePrimitiveIntegerTypes;
158+
public static ParseAsType ParsePrimitiveIntegerTypes
159+
{
160+
get
161+
{
162+
return (JsConfigScope.Current != null ? JsConfigScope.Current.ParsePrimitiveIntegerTypes : null)
163+
?? sParsePrimitiveIntegerTypes
164+
?? ParseAsType.Byte | ParseAsType.SByte | ParseAsType.Int16 | ParseAsType.UInt16 | ParseAsType.Int32 | ParseAsType.UInt32 | ParseAsType.Int64 | ParseAsType.UInt64;
165+
}
166+
set
167+
{
168+
if (!sParsePrimitiveIntegerTypes.HasValue) sParsePrimitiveIntegerTypes = value;
169+
}
170+
}
171+
135172
private static bool? sIncludeNullValues;
136173
public static bool IncludeNullValues
137174
{

src/ServiceStack.Text/JsConfigScope.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ public void Dispose()
5555
public bool? ConvertObjectTypesIntoStringDictionary { get; set; }
5656
public bool? TryToParsePrimitiveTypeValues { get; set; }
5757
public bool? TryToParseNumericType { get; set; }
58+
public ParseAsType? ParsePrimitiveFloatingPointTypes { get; set; }
59+
public ParseAsType? ParsePrimitiveIntegerTypes { get; set; }
5860
public bool? IncludeNullValues { get; set; }
5961
public bool? IncludeDefaultEnums { get; set; }
6062
public bool? TreatEnumAsInteger { get; set; }
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
3+
namespace ServiceStack.Text
4+
{
5+
[Flags]
6+
public enum ParseAsType
7+
{
8+
None = 0,
9+
Bool = 2,
10+
Byte = 4,
11+
SByte = 8,
12+
Int16 = 16,
13+
Int32 = 32,
14+
Int64 = 64,
15+
UInt16 = 128,
16+
UInt32 = 256,
17+
UInt64 = 512,
18+
Decimal = 1024,
19+
Double = 2048,
20+
Single = 4096
21+
}
22+
}

src/ServiceStack.Text/ServiceStack.Text.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@
303303
<Compile Include="XmlSerializer.cs">
304304
<SubType>Code</SubType>
305305
</Compile>
306+
<Compile Include="ParseAsType.cs" />
306307
</ItemGroup>
307308
<ItemGroup>
308309
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">

0 commit comments

Comments
 (0)