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

Commit ba307ad

Browse files
author
Antony Denyer
committed
Changed default behaviour of TryToParsePrimitiveTypeValues to always parse numeric types as either decimal, float or double.
This IMO is the safest choice as you shouldn't get any issues down the line working with decimals. It is also the what I would expect from json. Added config value TryToParseNumericType to allow the 'best' type to come back as per expected current behaviour Added unit tests around behaviour new and existing behaviour Reverted some changes from commit 9e8fd8e to fix behaviour for numeric type matching (looks like all unsigned numbers will be ulong) Fixed issues raised in: http://stackoverflow.com/questions/17368027/override-parseprimitive-in-servicestack-text http://stackoverflow.com/questions/13716838/how-to-get-servicestack-to-serialize-deserialize-an-expando-object-with-correc
1 parent 80e4c5b commit ba307ad

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)