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

Commit 8b834ec

Browse files
committed
Add support for deserializing tuples
1 parent e457126 commit 8b834ec

File tree

8 files changed

+120
-0
lines changed

8 files changed

+120
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System;
2+
using ServiceStack.Text.Json;
3+
4+
namespace ServiceStack.Text.Common
5+
{
6+
internal static class DeserializeCustomGenericType<TSerializer>
7+
where TSerializer : ITypeSerializer
8+
{
9+
private static readonly ITypeSerializer Serializer = JsWriter.GetTypeSerializer<TSerializer>();
10+
11+
public static ParseStringDelegate GetParseMethod(Type type)
12+
{
13+
if (type.Name.IndexOf("Tuple`", StringComparison.Ordinal) >= 0)
14+
return x => ParseTuple(type, x);
15+
16+
return null;
17+
}
18+
19+
public static object ParseTuple(Type tupleType, string value)
20+
{
21+
var index = 0;
22+
Serializer.EatMapStartChar(value, ref index);
23+
if (JsonTypeSerializer.IsEmptyMap(value, index))
24+
return tupleType.CreateInstance();
25+
26+
var genericArgs = tupleType.GetGenericArguments();
27+
var argValues = new object[genericArgs.Length];
28+
var valueLength = value.Length;
29+
while (index < valueLength)
30+
{
31+
var keyValue = Serializer.EatMapKey(value, ref index);
32+
Serializer.EatMapKeySeperator(value, ref index);
33+
var elementValue = Serializer.EatValue(value, ref index);
34+
if (keyValue == null) continue;
35+
36+
var keyIndex = keyValue.Substring("Item".Length).ToInt() - 1;
37+
var parseFn = Serializer.GetParseFn(genericArgs[keyIndex]);
38+
argValues[keyIndex] = parseFn(elementValue);
39+
40+
Serializer.EatItemSeperatorOrMapEndChar(value, ref index);
41+
}
42+
43+
var ctor = tupleType.GetConstructor(genericArgs);
44+
return ctor.Invoke(argValues);
45+
}
46+
}
47+
}

src/ServiceStack.Text/Common/JsReader.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ private ParseStringDelegate GetCoreParseFn<T>()
7575

7676
if (type.IsOrHasGenericInterfaceTypeOf(typeof(IEnumerable<>)))
7777
return DeserializeEnumerable<T, TSerializer>.Parse;
78+
79+
var customFn = DeserializeCustomGenericType<TSerializer>.GetParseMethod(type);
80+
if (customFn != null)
81+
return customFn;
7882
}
7983

8084
var pclParseFn = PclExport.Instance.GetJsReaderParseMethod<TSerializer>(typeof(T));

src/ServiceStack.Text/ServiceStack.Text.PCL.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
<Compile Include="AutoMappingUtils.cs" />
4343
<Compile Include="Build.Silverlight.cs" />
4444
<Compile Include="CollectionExtensions.cs" />
45+
<Compile Include="Common\DeserializeCustomGenericType.cs" />
4546
<Compile Include="Common\DeserializeKeyValuePair.cs" />
4647
<Compile Include="IStringSerializer.cs" />
4748
<Compile Include="Pcl.Dynamic.cs" />

src/ServiceStack.Text/ServiceStack.Text.SL5.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
<Compile Include="AutoMappingUtils.cs" />
6666
<Compile Include="Build.Silverlight.cs" />
6767
<Compile Include="CollectionExtensions.cs" />
68+
<Compile Include="Common\DeserializeCustomGenericType.cs" />
6869
<Compile Include="Common\DeserializeKeyValuePair.cs" />
6970
<Compile Include="IStringSerializer.cs" />
7071
<Compile Include="Pcl.Dynamic.cs" />

src/ServiceStack.Text/ServiceStack.Text.Signed.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
<Compile Include="AutoMappingUtils.cs" />
9898
<Compile Include="Build.Silverlight.cs" />
9999
<Compile Include="CollectionExtensions.cs" />
100+
<Compile Include="Common\DeserializeCustomGenericType.cs" />
100101
<Compile Include="Common\DeserializeKeyValuePair.cs" />
101102
<Compile Include="IStringSerializer.cs" />
102103
<Compile Include="Pcl.Dynamic.cs" />

src/ServiceStack.Text/ServiceStack.Text.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
<Compile Include="AutoMappingUtils.cs" />
9292
<Compile Include="Build.Silverlight.cs" />
9393
<Compile Include="CollectionExtensions.cs" />
94+
<Compile Include="Common\DeserializeCustomGenericType.cs" />
9495
<Compile Include="Common\DeserializeKeyValuePair.cs" />
9596
<Compile Include="IStringSerializer.cs" />
9697
<Compile Include="Pcl.Dynamic.cs" />

tests/ServiceStack.Text.Tests/ServiceStack.Text.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@
277277
<Compile Include="Support\BenchmarkTests.cs" />
278278
<Compile Include="Support\MovieDtos.cs" />
279279
<Compile Include="SystemTimeTests.cs" />
280+
<Compile Include="TupleTests.cs" />
280281
<Compile Include="UseCases\CentroidTests.cs" />
281282
<Compile Include="UseCases\GitHubRestTests.cs" />
282283
<Compile Include="UseCases\GithubV3ApiTests.cs" />
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
using NUnit.Framework;
3+
4+
namespace ServiceStack.Text.Tests
5+
{
6+
[TestFixture]
7+
public class TupleTests
8+
{
9+
[Test]
10+
public void can_get_single_tuple()
11+
{
12+
var tuple = Tuple.Create(true);
13+
var tstr = JsonSerializer.SerializeToString(tuple);
14+
Assert.True(tstr.Contains(":true"));
15+
16+
var deser = JsonSerializer.DeserializeFromString<Tuple<bool>>(tstr);
17+
Assert.True(deser.Item1); //fails
18+
}
19+
20+
[Test]
21+
public void can_get_double_tuple()
22+
{
23+
var tuple = Tuple.Create<bool, bool>(true, true);
24+
var tstr = JsonSerializer.SerializeToString(tuple);
25+
Assert.True(tstr.Contains(":true"));
26+
27+
var deser = JsonSerializer.DeserializeFromString<Tuple<bool, bool>>(tstr);
28+
Assert.True(deser.Item1); //fails
29+
Assert.True(deser.Item2);
30+
}
31+
32+
private class OtherType
33+
{
34+
public bool Item1 { get; set; }
35+
}
36+
37+
[Test]
38+
public void can_get_othertype()
39+
{
40+
var item = new OtherType() { Item1 = true };
41+
var tstr = JsonSerializer.SerializeToString(item);
42+
Assert.True(tstr.Contains(":true"));
43+
44+
var deser = JsonSerializer.DeserializeFromString<OtherType>(tstr);
45+
Assert.True(deser.Item1); //works
46+
}
47+
48+
private class OtherType<T>
49+
{
50+
public T Item1 { get; set; }
51+
}
52+
53+
[Test]
54+
public void can_get_othertype_typed()
55+
{
56+
var item = new OtherType<bool>() { Item1 = true };
57+
var tstr = JsonSerializer.SerializeToString(item);
58+
Assert.True(tstr.Contains(":true"));
59+
60+
var deser = JsonSerializer.DeserializeFromString<OtherType<bool>>(tstr);
61+
Assert.True(deser.Item1); //works
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)