Skip to content

Commit 4be0a7a

Browse files
committed
Merge pull request ServiceStack#86 from franklinwise/master
Added JsConfig option to propagate serialization exceptions up the stack rather than swallowing
2 parents e7828c8 + 46e29aa commit 4be0a7a

7 files changed

Lines changed: 114 additions & 7 deletions

File tree

src/ServiceStack.Text/Common/DeserializeTypeRefJson.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ internal static object StringToType(
9393
}
9494
catch
9595
{
96-
Tracer.Instance.WriteWarning("WARN: failed to set dynamic property {0} with: {1}", propertyName, propertyValueStr);
96+
if (JsConfig.ThrowOnDeserializationError) throw;
97+
else Tracer.Instance.WriteWarning("WARN: failed to set dynamic property {0} with: {1}", propertyName, propertyValueStr);
9798
}
9899
}
99100

@@ -106,7 +107,8 @@ internal static object StringToType(
106107
}
107108
catch
108109
{
109-
Tracer.Instance.WriteWarning("WARN: failed to set property {0} with: {1}", propertyName, propertyValueStr);
110+
if (JsConfig.ThrowOnDeserializationError) throw;
111+
else Tracer.Instance.WriteWarning("WARN: failed to set property {0} with: {1}", propertyName, propertyValueStr);
110112
}
111113
}
112114

src/ServiceStack.Text/Common/DeserializeTypeRefJsv.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ internal static object StringToType(
8484
}
8585
catch
8686
{
87-
Tracer.Instance.WriteWarning("WARN: failed to set dynamic property {0} with: {1}", propertyName, propertyValueStr);
87+
if (JsConfig.ThrowOnDeserializationError) throw;
88+
else Tracer.Instance.WriteWarning("WARN: failed to set dynamic property {0} with: {1}", propertyName, propertyValueStr);
8889
}
8990
}
9091

@@ -97,7 +98,8 @@ internal static object StringToType(
9798
}
9899
catch
99100
{
100-
Tracer.Instance.WriteWarning("WARN: failed to set property {0} with: {1}", propertyName, propertyValueStr);
101+
if (JsConfig.ThrowOnDeserializationError) throw;
102+
else Tracer.Instance.WriteWarning("WARN: failed to set property {0} with: {1}", propertyName, propertyValueStr);
101103
}
102104
}
103105

src/ServiceStack.Text/Common/WriteType.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,14 @@ public static void WriteProperties(TextWriter writer, object value)
213213
writer.Write(JsWriter.MapKeySeperator);
214214

215215
if (typeof (TSerializer) == typeof (JsonTypeSerializer)) JsState.IsWritingValue = true;
216-
propertyWriter.WriteFn(writer, propertyValue);
216+
if (propertyValue == null)
217+
{
218+
writer.Write(JsonUtils.Null);
219+
}
220+
else
221+
{
222+
propertyWriter.WriteFn(writer, propertyValue);
223+
}
217224
if (typeof(TSerializer) == typeof(JsonTypeSerializer)) JsState.IsWritingValue = false;
218225
}
219226
}

src/ServiceStack.Text/JsConfig.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,29 @@ public static bool EmitCamelCaseNames
105105
}
106106
}
107107

108+
/// <summary>
109+
/// Gets or sets a value indicating if the framework should throw serialization exceptions
110+
/// or continue regardless of deserialization errors. If <see langword="true"/> the framework
111+
/// will throw; otherwise, it will parse as many fields as possible. The default is <see langword="false"/>.
112+
/// </summary>
113+
[ThreadStatic]
114+
private static bool? tsThrowOnDeserializationError;
115+
private static bool? sThrowOnDeserializationError;
116+
public static bool ThrowOnDeserializationError
117+
{
118+
// obeying the use of ThreadStatic, but allowing for setting JsConfig once as is the normal case
119+
get
120+
{
121+
return tsThrowOnDeserializationError ?? sThrowOnDeserializationError ?? false;
122+
}
123+
set
124+
{
125+
bool theValue = value;
126+
if (!tsThrowOnDeserializationError.HasValue) tsThrowOnDeserializationError = value;
127+
if (!sThrowOnDeserializationError.HasValue) sThrowOnDeserializationError = value;
128+
}
129+
}
130+
108131
internal static HashSet<Type> HasSerializeFn = new HashSet<Type>();
109132

110133
public static void Reset()
@@ -114,6 +137,7 @@ public static void Reset()
114137
tsExcludeTypeInfo = sExcludeTypeInfo = null;
115138
tsEmitCamelCaseNames = sEmitCamelCaseNames = null;
116139
tsDateHandler = sDateHandler = null;
140+
tsThrowOnDeserializationError = sThrowOnDeserializationError = null;
117141
HasSerializeFn = new HashSet<Type>();
118142
}
119143

tests/ServiceStack.Text.Tests/JsonTests/BasicJsonTests.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,19 @@ public void Serialize_can_include_null_values()
9696
Type = "Programmer",
9797
SampleKey = 12,
9898
Nothing = null,
99-
NullableDateTime = null
99+
NullableDateTime = null,
100+
NullClass = null
100101
};
101102

102103
JsConfig.IncludeNullValues = true;
103104
var s = JsonSerializer.SerializeToString(o);
104105
JsConfig.Reset();
105-
Assert.That(s, Is.EqualTo("{\"Name\":\"Brandon\",\"Type\":\"Programmer\",\"SampleKey\":12,\"Nothing\":null,\"NullableDateTime\":null}"));
106+
Assert.That(s, Is.EqualTo("{\"Name\":\"Brandon\",\"Type\":\"Programmer\",\"SampleKey\":12,\"Nothing\":null,\"NullableDateTime\":null,\"NullClass\":null}"));
107+
}
108+
109+
private class NullClass
110+
{
111+
106112
}
107113

108114
[Test]
@@ -153,6 +159,8 @@ public string Nothing
153159
set;
154160
}
155161

162+
public NullClass NullClass { get; set; }
163+
156164
public DateTime? NullableDateTime { get; set; }
157165

158166
public NullValueTester()
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Runtime.Serialization;
4+
using NUnit.Framework;
5+
using ServiceStack.Text.Tests.Support;
6+
7+
namespace ServiceStack.Text.Tests.JsonTests
8+
{
9+
[TestFixture]
10+
public class ThrowOnDeserializeErrorTest
11+
{
12+
13+
[Test]
14+
public void TestThrows()
15+
{
16+
JsConfig.Reset();
17+
JsConfig.ThrowOnDeserializationError = true;
18+
19+
string json = @"{""idBad"":""abc"", ""idGood"":""2"" }";
20+
21+
bool threw = false;
22+
try
23+
{
24+
JsonSerializer.DeserializeFromString(json, typeof(TestDto));
25+
}
26+
catch (Exception)
27+
{
28+
threw = true;
29+
}
30+
31+
Assert.IsTrue(threw, "Should have thrown");
32+
}
33+
34+
[Test]
35+
public void TestDoesNotThrow()
36+
{
37+
JsConfig.Reset();
38+
JsConfig.ThrowOnDeserializationError = false;
39+
string json = @"{""idBad"":""abc"", ""idGood"":""2"" }";
40+
JsonSerializer.DeserializeFromString(json, typeof(TestDto));
41+
}
42+
43+
[Test]
44+
public void TestReset()
45+
{
46+
JsConfig.Reset();
47+
Assert.IsFalse(JsConfig.ThrowOnDeserializationError);
48+
JsConfig.ThrowOnDeserializationError = true;
49+
Assert.IsTrue(JsConfig.ThrowOnDeserializationError);
50+
JsConfig.Reset();
51+
Assert.IsFalse(JsConfig.ThrowOnDeserializationError);
52+
}
53+
54+
[DataContract]
55+
class TestDto
56+
{
57+
[DataMember(Name = "idGood")]
58+
public int IdGood { get; set; }
59+
[DataMember(Name = "idBad")]
60+
public int IdBad { get; set; }
61+
}
62+
}
63+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@
175175
<Compile Include="CsvTests\CustomHeaderTests.cs" />
176176
<Compile Include="JsonTests\JsonDateTimeTests.cs" />
177177
<Compile Include="JsonTests\PolymorphicListTests.cs" />
178+
<Compile Include="JsonTests\ThrowOnDeserializeErrorTest.cs" />
178179
<Compile Include="JsvTests\JsvDeserializeTypeTest.cs" />
179180
<Compile Include="MessagingTests.cs" />
180181
<Compile Include="NullableTypesTests.cs" />

0 commit comments

Comments
 (0)