Skip to content

Commit 574167e

Browse files
committed
Provide global-wide option to prevent serialization of __type info with JsConfig<T>.ExcludeTypeInfo and specific types with JsConfig<T>.ExcludeTypeInfo.
Ignore __type info for incompatible types, and deserialize based on target type instead.
1 parent 5b59734 commit 574167e

File tree

6 files changed

+126
-23
lines changed

6 files changed

+126
-23
lines changed

NuGet/lib/net35/ServiceStack.Text.dll

512 Bytes
Binary file not shown.

src/ServiceStack.Text/Common/DeserializeType.cs

+13-5
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ public static ParseStringDelegate GetParseMethod(Type type)
3838
var emptyCtorFn = ReflectionExtensions.GetConstructorMethodToCache(type);
3939
return value => emptyCtorFn();
4040
}
41-
42-
41+
4342
var setterMap = new Dictionary<string, SetPropertyDelegate>();
4443
var map = new Dictionary<string, ParseStringDelegate>();
4544

@@ -136,7 +135,16 @@ private static object StringToType(Type type, string strType,
136135
var typeName = Serializer.ParseString(propertyValueString);
137136
instance = ReflectionExtensions.CreateInstance(typeName);
138137
if (instance == null)
138+
{
139139
Tracer.Instance.WriteWarning("Could not find type: " + propertyValueString);
140+
}
141+
else
142+
{
143+
//If __type info doesn't match, ignore it.
144+
if (!type.IsAssignableFrom(instance.GetType()))
145+
instance = null;
146+
}
147+
140148
Serializer.EatItemSeperatorOrMapEndChar(strType, ref index);
141149
continue;
142150
}
@@ -157,14 +165,14 @@ private static object StringToType(Type type, string strType,
157165
{
158166
setterFn(instance, propertyValue);
159167
}
168+
169+
Serializer.EatItemSeperatorOrMapEndChar(strType, ref index);
170+
continue;
160171
}
161172
catch (Exception)
162173
{
163174
Tracer.Instance.WriteWarning("WARN: failed to set dynamic property {0} with: {1}", propertyName, propertyValueString);
164175
}
165-
166-
Serializer.EatItemSeperatorOrMapEndChar(strType, ref index);
167-
continue;
168176
}
169177

170178
parseStringFnMap.TryGetValue(propertyName, out parseStringFn);

src/ServiceStack.Text/Common/WriteType.cs

+11-9
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,19 @@ static WriteType()
3939

4040
public static void TypeInfoWriter(TextWriter writer, object obj)
4141
{
42-
if (obj == null) return;
42+
DidWriteTypeInfo(writer, obj);
43+
}
44+
45+
private static bool DidWriteTypeInfo(TextWriter writer, object obj)
46+
{
47+
if (obj == null
48+
|| JsConfig.ExcludeTypeInfo
49+
|| JsConfig<T>.ExcludeTypeInfo) return false;
4350

4451
Serializer.WriteRawString(writer, JsWriter.TypeAttr);
4552
writer.Write(JsWriter.MapKeySeperator);
4653
Serializer.WriteRawString(writer, obj.GetType().ToTypeString());
54+
return true;
4755
}
4856

4957
public static WriteObjectDelegate Write
@@ -113,16 +121,10 @@ public static void WriteProperties(TextWriter writer, object value)
113121
writer.Write(JsWriter.MapStartChar);
114122

115123
var i = 0;
116-
if (WriteTypeInfo != null)
124+
if (WriteTypeInfo != null || JsState.IsWritingDynamic)
117125
{
118-
WriteTypeInfo(writer, value);
119-
i++;
126+
if (DidWriteTypeInfo(writer, value)) i++;
120127
}
121-
else if (JsState.IsWritingDynamic)
122-
{
123-
TypeInfoWriter(writer, value);
124-
i++;
125-
}
126128

127129
if (PropertyWriters != null)
128130
{

src/ServiceStack.Text/JsConfig.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ static JsConfig()
2323
[ThreadStatic]
2424
public static bool IncludeNullValues = false;
2525

26+
[ThreadStatic]
27+
public static bool ExcludeTypeInfo = false;
28+
29+
public static void Reset()
30+
{
31+
ConvertObjectTypesIntoStringDictionary = IncludeNullValues = ExcludeTypeInfo = false;
32+
}
33+
2634
#if SILVERLIGHT || MONOTOUCH
2735
/// <summary>
2836
/// Provide hint to MonoTouch AOT compiler to pre-compile generic classes for all your DTOs.
@@ -172,7 +180,9 @@ public static void RegisterElement<T, TElement>()
172180
#endif
173181

174182
public class JsConfig<T> //where T : struct
175-
{
183+
{
184+
public static bool ExcludeTypeInfo = false;
185+
176186
/// <summary>
177187
/// Define custom serialization fn for BCL Structs
178188
/// </summary>

tests/ServiceStack.Text.Tests/InterfaceTests.cs

+21
Original file line numberDiff line numberDiff line change
@@ -167,5 +167,26 @@ public void Can_Serialize_User_OAuthSession_list()
167167
Assert.That(fromDto.ProviderOAuthAccess[0].Items.Count, Is.EqualTo(2));
168168
Assert.That(fromDto.ProviderOAuthAccess[1].Items.Count, Is.EqualTo(2));
169169
}
170+
171+
[Test]
172+
public void Doesnt_serialize_TypeInfo_when_set()
173+
{
174+
JsConfig.ExcludeTypeInfo = true;
175+
var userSession = new OAuthUserSession {
176+
Id = "1",
177+
CreatedAt = DateTime.UtcNow,
178+
LastModified = DateTime.UtcNow,
179+
ReferrerUrl = "http://referrer.com",
180+
ProviderOAuthAccess = new List<IOAuthTokens>
181+
{
182+
new OAuthTokens { Provider = "twitter", AccessToken = "TAccessToken", Items = { {"a","1"}, {"b","2"}, }},
183+
new OAuthTokens { Provider = "facebook", AccessToken = "FAccessToken", Items = { {"a","1"}, {"b","2"}, }},
184+
}
185+
};
186+
187+
Assert.That(userSession.ToJson().IndexOf("__type") == -1, Is.True);
188+
Assert.That(userSession.ToJsv().IndexOf("__type") == -1, Is.True);
189+
}
190+
170191
}
171192
}

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

+70-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using NUnit.Framework;
34
using ServiceStack.Text.Common;
45

56
namespace ServiceStack.Text.Tests.JsonTests
67
{
8+
public interface ICat
9+
{
10+
string Name { get; set; }
11+
}
12+
13+
public interface IDog
14+
{
15+
string Name { get; set; }
16+
}
17+
718
//[KnownType(typeof(Dog))]
819
//[KnownType(typeof(Cat))]
920
public abstract class Animal
@@ -15,7 +26,7 @@ public abstract string Name
1526
}
1627
}
1728

18-
public class Dog : Animal
29+
public class Dog : Animal, IDog
1930
{
2031
public override string Name
2132
{
@@ -24,7 +35,7 @@ public override string Name
2435
}
2536
}
2637

27-
public class Cat : Animal
38+
public class Cat : Animal, ICat
2839
{
2940
public override string Name
3041
{
@@ -60,6 +71,13 @@ public string Name
6071
[TestFixture]
6172
public class PolymorphicListTests : TestBase
6273
{
74+
[SetUp]
75+
public void SetUp()
76+
{
77+
JsConfig.Reset();
78+
JsConfig<ICat>.ExcludeTypeInfo = false;
79+
}
80+
6381
[Test]
6482
public void Can_serialise_polymorphic_list()
6583
{
@@ -76,7 +94,7 @@ public void Can_serialise_polymorphic_list()
7694
Assert.That(asText,
7795
Is.EqualTo(
7896
"[{\"__type\":\""
79-
+ typeof(Dog).ToTypeString()
97+
+ typeof(Dog).ToTypeString()
8098
+ "\",\"Name\":\"Fido\"},{\"__type\":\""
8199
+ typeof(Cat).ToTypeString()
82100
+ "\",\"Name\":\"Tigger\"}]"));
@@ -109,9 +127,9 @@ public void Can_deserialise_polymorphic_list()
109127
var list =
110128
JsonSerializer.DeserializeFromString<List<Animal>>(
111129
"[{\"__type\":\""
112-
+ typeof(Dog).ToTypeString()
130+
+ typeof(Dog).ToTypeString()
113131
+ "\",\"Name\":\"Fido\"},{\"__type\":\""
114-
+ typeof(Cat).ToTypeString()
132+
+ typeof(Cat).ToTypeString()
115133
+ "\",\"Name\":\"Tigger\"}]");
116134

117135
Assert.That(list.Count, Is.EqualTo(2));
@@ -129,9 +147,9 @@ public void Can_deserialise_an_entity_containing_a_polymorphic_list()
129147
var zoo =
130148
JsonSerializer.DeserializeFromString<Zoo>(
131149
"{\"Animals\":[{\"__type\":\""
132-
+ typeof(Dog).ToTypeString()
150+
+ typeof(Dog).ToTypeString()
133151
+ "\",\"Name\":\"Fido\"},{\"__type\":\""
134-
+ typeof(Cat).ToTypeString()
152+
+ typeof(Cat).ToTypeString()
135153
+ "\",\"Name\":\"Tigger\"}],\"Name\":\"City Zoo\"}");
136154

137155
Assert.That(zoo.Name, Is.EqualTo(@"City Zoo"));
@@ -144,5 +162,49 @@ public void Can_deserialise_an_entity_containing_a_polymorphic_list()
144162
Assert.That(animals[0].Name, Is.EqualTo(@"Fido"));
145163
Assert.That(animals[1].Name, Is.EqualTo(@"Tigger"));
146164
}
165+
166+
public class Pets
167+
{
168+
public ICat Cat { get; set; }
169+
public IDog Dog { get; set; }
170+
}
171+
172+
[Test]
173+
public void Can_exclude_specific_TypeInfo()
174+
{
175+
JsConfig<ICat>.ExcludeTypeInfo = true;
176+
var pets = new Pets {
177+
Cat = new Cat { Name = "Cat" },
178+
Dog = new Dog { Name = "Dog" },
179+
};
180+
181+
Assert.That(pets.ToJson(), Is.EqualTo(
182+
@"{""Cat"":{""Name"":""Cat""},""Dog"":{""__type"":""ServiceStack.Text.Tests.JsonTests.Dog, ServiceStack.Text.Tests"",""Name"":""Dog""}}"));
183+
}
184+
185+
public class PetDog
186+
{
187+
public IDog Dog { get; set; }
188+
}
189+
190+
public class WeirdCat
191+
{
192+
public Cat Dog { get; set; }
193+
}
194+
195+
[Test]
196+
public void Can_read_as_Cat_from_Dog_with_typeinfo()
197+
{
198+
var petDog = new PetDog { Dog = new Dog { Name = "Woof!" } };
199+
var json = petDog.ToJson();
200+
201+
Console.WriteLine(json);
202+
203+
var weirdCat = json.FromJson<WeirdCat>();
204+
205+
Assert.That(weirdCat.Dog, Is.Not.Null);
206+
Assert.That(weirdCat.Dog.Name, Is.EqualTo(petDog.Dog.Name));
207+
}
208+
147209
}
148210
}

0 commit comments

Comments
 (0)