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

Commit db9b209

Browse files
committed
Merge pull request #347 from antonydenyer/TryToParseNumericType
TryToParsePrimitiveTypeValues numeric values default to decimal
2 parents 80e4c5b + ba307ad commit db9b209

File tree

4 files changed

+147
-14
lines changed

4 files changed

+147
-14
lines changed

src/ServiceStack.Text/Common/DeserializeType.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -161,19 +161,19 @@ public static object ParsePrimitive(string value)
161161
decimal decimalValue;
162162
if (decimal.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out decimalValue))
163163
{
164+
if (!JsConfig.TryToParseNumericType)
165+
return decimalValue;
166+
164167
if (decimalValue == decimal.Truncate(decimalValue))
165-
{
166-
if (decimalValue <= ulong.MaxValue && decimalValue >= 0) return (ulong)decimalValue;
167-
if (decimalValue <= long.MaxValue && decimalValue >= long.MinValue)
168-
{
169-
var longValue = (long)decimalValue;
170-
if (longValue <= sbyte.MaxValue && longValue >= sbyte.MinValue) return (sbyte)longValue;
171-
if (longValue <= byte.MaxValue && longValue >= byte.MinValue) return (byte)longValue;
172-
if (longValue <= short.MaxValue && longValue >= short.MinValue) return (short)longValue;
173-
if (longValue <= ushort.MaxValue && longValue >= ushort.MinValue) return (ushort)longValue;
174-
if (longValue <= int.MaxValue && longValue >= int.MinValue) return (int)longValue;
175-
if (longValue <= uint.MaxValue && longValue >= uint.MinValue) return (uint)longValue;
176-
}
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;
177177
}
178178
return decimalValue;
179179
}

src/ServiceStack.Text/JsConfig.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public static JsConfigScope BeginScope()
3131
public static JsConfigScope With(
3232
bool? convertObjectTypesIntoStringDictionary = null,
3333
bool? tryToParsePrimitiveTypeValues = null,
34+
bool? tryToParseNumericType = null,
3435
bool? includeNullValues = null,
3536
bool? excludeTypeInfo = null,
3637
bool? includeTypeInfo = null,
@@ -54,6 +55,7 @@ public static JsConfigScope With(
5455
return new JsConfigScope {
5556
ConvertObjectTypesIntoStringDictionary = convertObjectTypesIntoStringDictionary ?? sConvertObjectTypesIntoStringDictionary,
5657
TryToParsePrimitiveTypeValues = tryToParsePrimitiveTypeValues ?? sTryToParsePrimitiveTypeValues,
58+
TryToParseNumericType = tryToParseNumericType ?? sTryToParseNumericType,
5759
IncludeNullValues = includeNullValues ?? sIncludeNullValues,
5860
ExcludeTypeInfo = excludeTypeInfo ?? sExcludeTypeInfo,
5961
IncludeTypeInfo = includeTypeInfo ?? sIncludeTypeInfo,
@@ -106,6 +108,21 @@ public static bool TryToParsePrimitiveTypeValues
106108
}
107109
}
108110

111+
private static bool? sTryToParseNumericType;
112+
public static bool TryToParseNumericType
113+
{
114+
get
115+
{
116+
return (JsConfigScope.Current != null ? JsConfigScope.Current.TryToParseNumericType : null)
117+
?? sTryToParseNumericType
118+
?? false;
119+
}
120+
set
121+
{
122+
if (!sTryToParseNumericType.HasValue) sTryToParseNumericType = value;
123+
}
124+
}
125+
109126
private static bool? sIncludeNullValues;
110127
public static bool IncludeNullValues
111128
{
@@ -506,7 +523,8 @@ public static EmptyCtorFactoryDelegate ModelFactory
506523
}
507524
}
508525

509-
public static void Reset()
526+
527+
public static void Reset()
510528
{
511529
foreach (var rawSerializeType in HasSerializeFn.ToArray())
512530
{
@@ -515,6 +533,7 @@ public static void Reset()
515533

516534
sModelFactory = ReflectionExtensions.GetConstructorMethodToCache;
517535
sTryToParsePrimitiveTypeValues = null;
536+
sTryToParseNumericType = null;
518537
sConvertObjectTypesIntoStringDictionary = null;
519538
sIncludeNullValues = null;
520539
sExcludeTypeInfo = null;

src/ServiceStack.Text/JsConfigScope.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public void Dispose()
5757

5858
public bool? ConvertObjectTypesIntoStringDictionary { get; set; }
5959
public bool? TryToParsePrimitiveTypeValues { get; set; }
60+
public bool? TryToParseNumericType { get; set; }
6061
public bool? IncludeNullValues { get; set; }
6162
public bool? TreatEnumAsInteger { get; set; }
6263
public bool? ExcludeTypeInfo { get; set; }

tests/ServiceStack.Text.Tests/DictionaryTests.cs

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ private static Dictionary<string, object> SetupDict()
179179
{ "a", "text" },
180180
{ "b", 32 },
181181
{ "c", false },
182-
{ "d", new[] {1, 2, 3} }
182+
{ "d", new[] {1, 2, 3} },
183+
{ "e", 1m },
183184
};
184185
}
185186

@@ -244,6 +245,118 @@ public void Test_ServiceStack_Text_JsonSerializer_Array_Value_Deserializes_Corre
244245
Assert.AreEqual(new List<int> {1, 2, 3}, deserializedDict["d"]);
245246
}
246247

248+
249+
[Test]
250+
public void deserizes_to_decimal_by_default()
251+
{
252+
JsConfig.TryToParsePrimitiveTypeValues = true;
253+
254+
var dict = SetupDict();
255+
var json = JsonSerializer.SerializeToString(dict);
256+
var deserializedDict = JsonSerializer.DeserializeFromString<IDictionary<string, object>>(json);
257+
Assert.That(deserializedDict["e"], Is.TypeOf<decimal>());
258+
Assert.That(deserializedDict["e"],Is.EqualTo(1m));
259+
260+
}
261+
class NumericType
262+
{
263+
264+
public NumericType(decimal max, Type type)
265+
: this(0,max,type)
266+
{
267+
268+
}
269+
public NumericType(decimal min,decimal max,Type type)
270+
{
271+
Min = min;
272+
Max = max;
273+
Type = type;
274+
}
275+
276+
public decimal Min { get; private set; }
277+
public decimal Max { get; private set; }
278+
public Type Type { get; private set; }
279+
}
280+
281+
[Test]
282+
public void deserizes_signed_bytes_into_to_best_fit_numeric()
283+
{
284+
JsConfig.TryToParsePrimitiveTypeValues = true;
285+
JsConfig.TryToParseNumericType = true;
286+
287+
var deserializedDict = JsonSerializer.DeserializeFromString<IDictionary<string, object>>("{\"min\":-128,\"max\":127}");
288+
Assert.That(deserializedDict["min"], Is.TypeOf<sbyte>());
289+
Assert.That(deserializedDict["min"], Is.EqualTo(sbyte.MinValue));
290+
//it seemed strange having zero return as a signed byte
291+
Assert.That(deserializedDict["max"], Is.TypeOf<byte>());
292+
Assert.That(deserializedDict["max"], Is.EqualTo(sbyte.MaxValue));
293+
}
294+
295+
[Test]
296+
public void deserizes_signed_types_into_to_best_fit_numeric()
297+
{
298+
var unsignedTypes = new[]
299+
{
300+
new NumericType(Int16.MinValue,Int16.MaxValue, typeof (Int16)),
301+
new NumericType(Int32.MinValue,Int32.MaxValue, typeof (Int32)),
302+
new NumericType(Int64.MinValue,Int64.MaxValue, typeof (Int64)),
303+
};
304+
305+
JsConfig.TryToParsePrimitiveTypeValues = true;
306+
JsConfig.TryToParseNumericType = true;
307+
308+
309+
foreach (var signedType in unsignedTypes)
310+
{
311+
var dict = new Dictionary<string, object>
312+
{
313+
{"min",signedType.Min},
314+
{"max",signedType.Max},
315+
};
316+
317+
var json = JsonSerializer.SerializeToString(dict);
318+
var deserializedDict = JsonSerializer.DeserializeFromString<IDictionary<string, object>>(json);
319+
Assert.That(deserializedDict["min"], Is.TypeOf(signedType.Type));
320+
Assert.That(deserializedDict["min"], Is.EqualTo(signedType.Min));
321+
Assert.That(deserializedDict["max"], Is.TypeOf(signedType.Type));
322+
Assert.That(deserializedDict["max"], Is.EqualTo(signedType.Max));
323+
324+
}
325+
}
326+
327+
[Test]
328+
public void deserizes_unsigned_types_into_to_best_fit_numeric()
329+
{
330+
var unsignedTypes = new[]
331+
{
332+
new NumericType(byte.MinValue,byte.MaxValue, typeof (byte)),
333+
new NumericType(UInt16.MaxValue, typeof (UInt16)),
334+
new NumericType(UInt32.MaxValue, typeof (UInt32)),
335+
new NumericType(UInt64.MaxValue, typeof (UInt64)),
336+
};
337+
338+
JsConfig.TryToParsePrimitiveTypeValues = true;
339+
JsConfig.TryToParseNumericType = true;
340+
341+
342+
foreach (var unsignedType in unsignedTypes)
343+
{
344+
var dict = new Dictionary<string, object>
345+
{
346+
{"min",unsignedType.Min},
347+
{"max",unsignedType.Max},
348+
};
349+
350+
var json = JsonSerializer.SerializeToString(dict);
351+
var deserializedDict = JsonSerializer.DeserializeFromString<IDictionary<string, object>>(json);
352+
Assert.That(deserializedDict["min"], Is.EqualTo(0));
353+
Assert.That(deserializedDict["min"], Is.TypeOf<byte>());
354+
Assert.That(deserializedDict["max"], Is.TypeOf(unsignedType.Type));
355+
Assert.That(deserializedDict["max"], Is.EqualTo(unsignedType.Max));
356+
357+
}
358+
}
359+
247360
[Test]
248361
public void Can_deserialize_mixed_dictionary_into_strongtyped_map()
249362
{

0 commit comments

Comments
 (0)