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

Commit

Permalink
Merge pull request #429 from scottmcarthur/NumberParsing
Browse files Browse the repository at this point in the history
Number parsing
  • Loading branch information
mythz committed Aug 14, 2014
2 parents 7f7e67e + 6c46779 commit 50cc842
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 44 deletions.
101 changes: 57 additions & 44 deletions src/ServiceStack.Text/Common/DeserializeType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,50 +149,63 @@ public static object ParseQuotedPrimitive(string value)
return Serializer.UnescapeString(value);
}

public static object ParsePrimitive(string value)
{
if (string.IsNullOrEmpty(value)) return null;

bool boolValue;
if (bool.TryParse(value, out boolValue)) return boolValue;
float floatValue;
double doubleValue;

decimal decimalValue;
if (decimal.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out decimalValue))
{
if (!JsConfig.TryToParseNumericType)
return decimalValue;

if (decimalValue == decimal.Truncate(decimalValue))
{
if (decimalValue <= byte.MaxValue && decimalValue >= byte.MinValue) return (byte)decimalValue;
if (decimalValue <= sbyte.MaxValue && decimalValue >= sbyte.MinValue) return (sbyte)decimalValue;
if (decimalValue <= Int16.MaxValue && decimalValue >= Int16.MinValue) return (Int16)decimalValue;
if (decimalValue <= UInt16.MaxValue && decimalValue >= UInt16.MinValue) return (UInt16)decimalValue;
if (decimalValue <= Int32.MaxValue && decimalValue >= Int32.MinValue) return (Int32)decimalValue;
if (decimalValue <= UInt32.MaxValue && decimalValue >= UInt32.MinValue) return (UInt32)decimalValue;
if (decimalValue <= Int64.MaxValue && decimalValue >= Int64.MinValue) return (Int64)decimalValue;
if (decimalValue <= UInt64.MaxValue && decimalValue >= UInt64.MinValue) return (UInt64)decimalValue;
}

if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out floatValue))
return floatValue;

if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out doubleValue))
return doubleValue;

return decimalValue;
}

if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out floatValue))
return floatValue;

if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out doubleValue))
return doubleValue;

return null;
}
public static object ParsePrimitive(string value)
{
if (string.IsNullOrEmpty(value)) return null;

bool boolValue;
if (bool.TryParse(value, out boolValue)) return boolValue;

// Parse as decimal
decimal decimalValue;
var acceptDecimal = JsConfig.ParsePrimitiveFloatingPointTypes.HasFlag(ParseAsType.Decimal);
var hasDecimal = decimal.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out decimalValue);

// Check if the number is an Primitive Integer type given that we have a decimal
if(hasDecimal && decimalValue == decimal.Truncate(decimalValue))
{
// Value is a whole number
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.Byte) && decimalValue <= byte.MaxValue && decimalValue >= byte.MinValue) return (byte)decimalValue;
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.SByte) && decimalValue <= sbyte.MaxValue && decimalValue >= sbyte.MinValue) return (sbyte)decimalValue;
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.Int16) && decimalValue <= Int16.MaxValue && decimalValue >= Int16.MinValue) return (Int16)decimalValue;
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.UInt16) && decimalValue <= UInt16.MaxValue && decimalValue >= UInt16.MinValue) return (UInt16)decimalValue;
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.Int32) && decimalValue <= Int32.MaxValue && decimalValue >= Int32.MinValue) return (Int32)decimalValue;
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.UInt32) && decimalValue <= UInt32.MaxValue && decimalValue >= UInt32.MinValue) return (UInt32)decimalValue;
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.Int64) && decimalValue <= Int64.MaxValue && decimalValue >= Int64.MinValue) return (Int64)decimalValue;
if (JsConfig.ParsePrimitiveIntegerTypes.HasFlag(ParseAsType.UInt64) && decimalValue <= UInt64.MaxValue && decimalValue >= UInt64.MinValue) return (UInt64)decimalValue;
return null;
}

// Value is a floating point number

// Return a decimal if the user accepts a decimal
if(hasDecimal && acceptDecimal)
return decimalValue;

// Parse as double if decimal failed or user wants a double
double doubleValue = 0;
var acceptDouble = JsConfig.ParsePrimitiveFloatingPointTypes.HasFlag(ParseAsType.Double);
var hasDouble = (!hasDecimal || acceptDouble) && double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out doubleValue);

// Return a double if the user accepts a double
if(acceptDouble && hasDouble)
return doubleValue;

// Parse as float
float floatValue;
var acceptFloat = JsConfig.ParsePrimitiveFloatingPointTypes.HasFlag(ParseAsType.Single);
var hasFloat = float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out floatValue);

// Return a float if the user accepts a float
if(acceptFloat && hasFloat)
return floatValue;

// Default to decimal, then double , then float or null
if(hasDecimal) return decimalValue;
if(hasDouble) return doubleValue;
if(hasFloat) return floatValue;
return null;
}

internal static object ParsePrimitive(string value, char firstChar)
{
Expand Down
37 changes: 37 additions & 0 deletions src/ServiceStack.Text/JsConfig.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
Expand Down Expand Up @@ -30,6 +31,8 @@ public static JsConfigScope BeginScope()
bool? convertObjectTypesIntoStringDictionary = null,
bool? tryToParsePrimitiveTypeValues = null,
bool? tryToParseNumericType = null,
ParseAsType? parsePrimitiveFloatingPointTypes = null,
ParseAsType? parsePrimitiveIntegerTypes = null,
bool? includeNullValues = null,
bool? includeDefaultEnums = null,
bool? excludeTypeInfo = null,
Expand Down Expand Up @@ -60,6 +63,10 @@ public static JsConfigScope BeginScope()
ConvertObjectTypesIntoStringDictionary = convertObjectTypesIntoStringDictionary ?? sConvertObjectTypesIntoStringDictionary,
TryToParsePrimitiveTypeValues = tryToParsePrimitiveTypeValues ?? sTryToParsePrimitiveTypeValues,
TryToParseNumericType = tryToParseNumericType ?? sTryToParseNumericType,

ParsePrimitiveFloatingPointTypes = parsePrimitiveFloatingPointTypes ?? sParsePrimitiveFloatingPointTypes,
ParsePrimitiveIntegerTypes = parsePrimitiveIntegerTypes ?? sParsePrimitiveIntegerTypes,

IncludeNullValues = includeNullValues ?? sIncludeNullValues,
IncludeDefaultEnums = includeDefaultEnums ?? sIncludeDefaultEnums,
ExcludeTypeInfo = excludeTypeInfo ?? sExcludeTypeInfo,
Expand Down Expand Up @@ -132,6 +139,36 @@ public static bool TryToParseNumericType
}
}

private static ParseAsType? sParsePrimitiveFloatingPointTypes;
public static ParseAsType ParsePrimitiveFloatingPointTypes
{
get
{
return (JsConfigScope.Current != null ? JsConfigScope.Current.ParsePrimitiveFloatingPointTypes : null)
?? sParsePrimitiveFloatingPointTypes
?? ParseAsType.Decimal;
}
set
{
if (sParsePrimitiveFloatingPointTypes == null) sParsePrimitiveFloatingPointTypes = value;
}
}

private static ParseAsType? sParsePrimitiveIntegerTypes;
public static ParseAsType ParsePrimitiveIntegerTypes
{
get
{
return (JsConfigScope.Current != null ? JsConfigScope.Current.ParsePrimitiveIntegerTypes : null)
?? sParsePrimitiveIntegerTypes
?? ParseAsType.Byte | ParseAsType.SByte | ParseAsType.Int16 | ParseAsType.UInt16 | ParseAsType.Int32 | ParseAsType.UInt32 | ParseAsType.Int64 | ParseAsType.UInt64;
}
set
{
if (!sParsePrimitiveIntegerTypes.HasValue) sParsePrimitiveIntegerTypes = value;
}
}

private static bool? sIncludeNullValues;
public static bool IncludeNullValues
{
Expand Down
2 changes: 2 additions & 0 deletions src/ServiceStack.Text/JsConfigScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public void Dispose()
public bool? ConvertObjectTypesIntoStringDictionary { get; set; }
public bool? TryToParsePrimitiveTypeValues { get; set; }
public bool? TryToParseNumericType { get; set; }
public ParseAsType? ParsePrimitiveFloatingPointTypes { get; set; }
public ParseAsType? ParsePrimitiveIntegerTypes { get; set; }
public bool? IncludeNullValues { get; set; }
public bool? IncludeDefaultEnums { get; set; }
public bool? TreatEnumAsInteger { get; set; }
Expand Down
22 changes: 22 additions & 0 deletions src/ServiceStack.Text/ParseAsType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;

namespace ServiceStack.Text
{
[Flags]
public enum ParseAsType
{
None = 0,
Bool = 2,
Byte = 4,
SByte = 8,
Int16 = 16,
Int32 = 32,
Int64 = 64,
UInt16 = 128,
UInt32 = 256,
UInt64 = 512,
Decimal = 1024,
Double = 2048,
Single = 4096
}
}
1 change: 1 addition & 0 deletions src/ServiceStack.Text/ServiceStack.Text.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@
<Compile Include="XmlSerializer.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ParseAsType.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
Expand Down

0 comments on commit 50cc842

Please sign in to comment.