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

Commit

Permalink
Normalize behavior of parsing nullable booleans
Browse files Browse the repository at this point in the history
  • Loading branch information
mythz committed Jul 17, 2018
1 parent 36a2cc9 commit 768d86b
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 17 deletions.
20 changes: 4 additions & 16 deletions src/ServiceStack.Text/Common/DeserializeBuiltin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,7 @@ private static ParseStringSpanDelegate GetParseStringSpanFn()
switch (typeCode)
{
case TypeCode.Boolean:
//Lots of kids like to use '1', HTML checkboxes use 'on' as a soft convention
return value =>
value.Length == 1 ?
value[0] == '1'
: value.Length == 2 ?
value[0] == 'o' && value[1] == 'n' :
value.ParseBoolean();

return value => value.ParseBoolean();
case TypeCode.SByte:
return SignedInteger<sbyte>.ParseObject;
case TypeCode.Byte:
Expand Down Expand Up @@ -87,14 +80,9 @@ private static ParseStringSpanDelegate GetParseStringSpanFn()
switch (typeCode)
{
case TypeCode.Boolean:
return value => value.IsNullOrEmpty() ?
(bool?)null
: value.Length == 1 ?
value[0] == '1'
: value.Length == 2 ?
value[0] == 'o' && value[1] == 'n' :
value.ParseBoolean();

return value => value.IsNullOrEmpty()
? (bool?)null
: value.ParseBoolean();
case TypeCode.SByte:
return SignedInteger<sbyte>.ParseNullableObject;
case TypeCode.Byte:
Expand Down
37 changes: 36 additions & 1 deletion src/ServiceStack.Text/StringSpanExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,42 @@ public static ReadOnlySpan<char> FromCsvField(this ReadOnlySpan<char> text)
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool ParseBoolean(this ReadOnlySpan<char> value) => MemoryProvider.Instance.ParseBoolean(value);
public static bool ParseBoolean(this ReadOnlySpan<char> value)
{
//Lots of kids like to use '1', HTML checkboxes use 'on' as a soft convention
switch (value.Length)
{
case 0:
return false;
case 1:
switch (value[0])
{
case '1':
case 't':
case 'T':
case 'y':
case 'Y':
return true;
case '0':
case 'f':
case 'F':
case 'n':
case 'N':
return false;
}
break;
case 2:
if (value[0] == 'o' && value[1] == 'n')
return true;
break;
case 3:
if (value[0] == 'o' && value[1] == 'f' && value[1] == 'f')
return false;
break;
}

return MemoryProvider.Instance.ParseBoolean(value);
}

public static bool TryParseBoolean(this ReadOnlySpan<char> value, out bool result) =>
MemoryProvider.Instance.TryParseBoolean(value, out result);
Expand Down
54 changes: 54 additions & 0 deletions tests/ServiceStack.Text.Tests/Issues/NullableIssues.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using NUnit.Framework;

namespace ServiceStack.Text.Tests.Issues
{
public class NullableIssues
{
public class NBoolTest
{
public bool? IsOk {get; set;}

protected bool Equals(NBoolTest other) => IsOk == other.IsOk;
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((NBoolTest) obj);
}
public override int GetHashCode() => IsOk.GetHashCode();
}

[Test]
public void Does_deserialize_nullable_bools()
{
Assert.That("{\"IsOk\": true}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = true }));
Assert.That("{\"IsOk\": false}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = false }));
Assert.That("{\"IsOk\": \"true\"}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = true }));
Assert.That("{\"IsOk\": \"false\"}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = false }));
Assert.That("{\"IsOk\": \"True\"}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = true }));
Assert.That("{\"IsOk\": \"False\"}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = false }));

Assert.That("{\"IsOk\": null}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = null }));
}

[Test]
public void Does_deserialize_nullable_bools_conventions()
{
Assert.That("{\"IsOk\": \"t\"}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = true }));
Assert.That("{\"IsOk\": \"f\"}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = false }));
Assert.That("{\"IsOk\": \"Y\"}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = true }));
Assert.That("{\"IsOk\": \"N\"}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = false }));
Assert.That("{\"IsOk\": \"on\"}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = true }));
Assert.That("{\"IsOk\": \"off\"}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = false }));
}

[Test]
public void Deserialize_nullable_bools_results_in_error()
{
Assert.That("{\"IsOk\": \"tt\"}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = null }));
Assert.That("{\"IsOk\": \"fu\"}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = null }));
Assert.That("{\"IsOk\": \"eee\"}".FromJson<NBoolTest>(), Is.EqualTo(new NBoolTest { IsOk = null }));
}
}
}

0 comments on commit 768d86b

Please sign in to comment.