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

Commit f62cf88

Browse files
committed
Added support for DataMember(Order)
- sorting propertyWriters according to Order on DataMember attribute - processing DataMember attribute of fields as well. - added reflection extensions for fields
1 parent 9cb0a0a commit f62cf88

File tree

6 files changed

+97
-26
lines changed

6 files changed

+97
-26
lines changed

src/ServiceStack.Text/Common/WriteType.cs

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ private static bool Init()
125125
var propertyInfo = propertyInfos[i];
126126

127127
string propertyName, propertyNameCLSFriendly, propertyNameLowercaseUnderscore, propertyReflectedName;
128+
int propertyOrder = 0;
128129

129130
if (isDataContract)
130131
{
@@ -135,6 +136,7 @@ private static bool Init()
135136
propertyNameCLSFriendly = dcsDataMember.Name ?? propertyName.ToCamelCase();
136137
propertyNameLowercaseUnderscore = dcsDataMember.Name ?? propertyName.ToLowercaseUnderscore();
137138
propertyReflectedName = dcsDataMember.Name ?? propertyInfo.ReflectedType.Name;
139+
propertyOrder = dcsDataMember.Order;
138140
}
139141
else
140142
{
@@ -155,6 +157,7 @@ private static bool Init()
155157
propertyReflectedName,
156158
propertyNameCLSFriendly,
157159
propertyNameLowercaseUnderscore,
160+
propertyOrder,
158161
propertyInfo.GetValueGetter<T>(),
159162
Serializer.GetWriteFn(propertyType),
160163
suppressDefaultValue
@@ -165,28 +168,51 @@ private static bool Init()
165168
{
166169
var fieldInfo = fieldInfos[i];
167170

168-
string propertyName = fieldInfo.Name;
169-
string propertyNameCLSFriendly = propertyName.ToCamelCase();
170-
string propertyNameLowercaseUnderscore = propertyName.ToLowercaseUnderscore();
171-
string propertyReflectedName = fieldInfo.ReflectedType.Name;
171+
string propertyName, propertyNameCLSFriendly, propertyNameLowercaseUnderscore, propertyReflectedName;
172+
int propertyOrder = 0;
172173

173174
var propertyType = fieldInfo.FieldType;
174175
var suppressDefaultValue = propertyType.IsValueType() && JsConfig.HasSerializeFn.Contains(propertyType)
175176
? propertyType.GetDefaultValue()
176177
: null;
177178

179+
if (isDataContract)
180+
{
181+
var dcsDataMember = fieldInfo.GetDataMember();
182+
if (dcsDataMember == null) continue;
183+
184+
propertyName = dcsDataMember.Name ?? fieldInfo.Name;
185+
propertyNameCLSFriendly = dcsDataMember.Name ?? propertyName.ToCamelCase();
186+
propertyNameLowercaseUnderscore = dcsDataMember.Name ?? propertyName.ToLowercaseUnderscore();
187+
propertyReflectedName = dcsDataMember.Name ?? fieldInfo.ReflectedType.Name;
188+
propertyOrder = dcsDataMember.Order;
189+
}
190+
else
191+
{
192+
propertyName = fieldInfo.Name;
193+
propertyNameCLSFriendly = propertyName.ToCamelCase();
194+
propertyNameLowercaseUnderscore = propertyName.ToLowercaseUnderscore();
195+
propertyReflectedName = fieldInfo.ReflectedType.Name;
196+
}
197+
198+
199+
178200
PropertyWriters[i + propertyNamesLength] = new TypePropertyWriter
179201
(
180202
propertyName,
181203
propertyReflectedName,
182204
propertyNameCLSFriendly,
183205
propertyNameLowercaseUnderscore,
206+
propertyOrder,
184207
fieldInfo.GetValueGetter<T>(),
185208
Serializer.GetWriteFn(propertyType),
186209
suppressDefaultValue
187210
);
188211
}
189212

213+
214+
PropertyWriters = PropertyWriters.OrderBy(x => x.propertyOrder).ToArray();
215+
190216
return true;
191217
}
192218

@@ -204,6 +230,7 @@ internal string PropertyName
204230
}
205231
}
206232
internal readonly string propertyName;
233+
internal readonly int propertyOrder;
207234
internal readonly string propertyReflectedName;
208235
internal readonly string propertyCombinedNameUpper;
209236
internal readonly string propertyNameCLSFriendly;
@@ -212,10 +239,11 @@ internal string PropertyName
212239
internal readonly WriteObjectDelegate WriteFn;
213240
internal readonly object DefaultValue;
214241

215-
public TypePropertyWriter(string propertyName, string propertyReflectedName, string propertyNameCLSFriendly, string propertyNameLowercaseUnderscore,
242+
public TypePropertyWriter(string propertyName, string propertyReflectedName, string propertyNameCLSFriendly, string propertyNameLowercaseUnderscore, int propertyOrder,
216243
Func<T, object> getterFn, WriteObjectDelegate writeFn, object defaultValue)
217244
{
218245
this.propertyName = propertyName;
246+
this.propertyOrder = propertyOrder;
219247
this.propertyReflectedName = propertyReflectedName;
220248
this.propertyCombinedNameUpper = propertyReflectedName.ToUpper() + "." + propertyName.ToUpper();
221249
this.propertyNameCLSFriendly = propertyNameCLSFriendly;

src/ServiceStack.Text/ReflectionExtensions.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,18 @@ public static DataMemberAttribute GetDataMember(this PropertyInfo pi)
574574
var dataMember = pi.CustomAttributes(typeof(DataMemberAttribute), false)
575575
.FirstOrDefault() as DataMemberAttribute;
576576

577+
#if !SILVERLIGHT && !MONOTOUCH && !XBOX
578+
if (dataMember == null && Env.IsMono)
579+
return pi.GetWeakDataMember();
580+
#endif
581+
return dataMember;
582+
}
583+
584+
public static DataMemberAttribute GetDataMember(this FieldInfo pi)
585+
{
586+
var dataMember = pi.CustomAttributes(typeof(DataMemberAttribute), false)
587+
.FirstOrDefault() as DataMemberAttribute;
588+
577589
#if !SILVERLIGHT && !MONOTOUCH && !XBOX
578590
if (dataMember == null && Env.IsMono)
579591
return pi.GetWeakDataMember();
@@ -632,6 +644,36 @@ public static DataMemberAttribute GetWeakDataMember(this PropertyInfo pi)
632644
}
633645
return null;
634646
}
647+
648+
public static DataMemberAttribute GetWeakDataMember(this FieldInfo pi)
649+
{
650+
var attr = pi.CustomAttributes().FirstOrDefault(x => x.GetType().Name == DataMember);
651+
if (attr != null)
652+
{
653+
var attrType = attr.GetType();
654+
655+
FastMember.TypeAccessor accessor;
656+
lock (typeAccessorMap)
657+
{
658+
if (!typeAccessorMap.TryGetValue(attrType, out accessor))
659+
typeAccessorMap[attrType] = accessor = FastMember.TypeAccessor.Create(attr.GetType());
660+
}
661+
662+
var newAttr = new DataMemberAttribute
663+
{
664+
Name = (string)accessor[attr, "Name"],
665+
EmitDefaultValue = (bool)accessor[attr, "EmitDefaultValue"],
666+
IsRequired = (bool)accessor[attr, "IsRequired"],
667+
};
668+
669+
var order = (int)accessor[attr, "Order"];
670+
if (order >= 0)
671+
newAttr.Order = order; //Throws Exception if set to -1
672+
673+
return newAttr;
674+
}
675+
return null;
676+
}
635677
#endif
636678
}
637679

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public void Does_serialize_To_CamelCase()
3939
var json = dto.ToJson();
4040

4141
Assert.That(json, Is.EqualTo(
42-
"{\"id\":1,\"imdbId\":\"tt0111161\",\"title\":\"The Shawshank Redemption\",\"rating\":9.2,\"director\":\"Frank Darabont\",\"releaseDate\":\"\\/Date(792979200000)\\/\",\"tagLine\":\"Fear can hold you prisoner. Hope can set you free.\",\"genres\":[\"Crime\",\"Drama\"]}"));
42+
"{\"id\":1,\"title\":\"The Shawshank Redemption\",\"imdbId\":\"tt0111161\",\"rating\":9.2,\"director\":\"Frank Darabont\",\"releaseDate\":\"\\/Date(792979200000)\\/\",\"tagLine\":\"Fear can hold you prisoner. Hope can set you free.\",\"genres\":[\"Crime\",\"Drama\"]}"));
4343

4444
Serialize(dto);
4545
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.Collections.Generic;
33
using NUnit.Framework;
44
using ServiceStack.Client;
5-
using ServiceStack.Common.Tests.Models;
5+
using ServiceStack.Text.Tests.Support;
66

77
namespace ServiceStack.Text.Tests.JsonTests
88
{
@@ -14,7 +14,7 @@ public class JsonDataContractCompatibilityTests
1414
[Test]
1515
public void Can_serialize_a_movie()
1616
{
17-
const string clientJson = "{\"Id\":\"tt0110912\",\"Title\":\"Pulp Fiction\",\"Rating\":\"8.9\",\"Director\":\"Quentin Tarantino\",\"ReleaseDate\":\"/Date(785635200000)/\",\"TagLine\":\"Girls like me don't make invitations like this to just anyone!\",\"Genres\":[\"Crime\",\"Drama\",\"Thriller\"]}";
17+
const string clientJson = "{\"Id\":\"0110912\",\"ImdbId\":\"tt0111161\",\"Title\":\"Pulp Fiction\",\"Rating\":\"8.9\",\"Director\":\"Quentin Tarantino\",\"ReleaseDate\":\"/Date(785635200000)/\",\"TagLine\":\"Girls like me don't make invitations like this to just anyone!\",\"Genres\":[\"Crime\",\"Drama\",\"Thriller\"]}";
1818
var jsonModel = JsonSerializer.DeserializeFromString<Movie>(clientJson);
1919
var bclJsonModel = BclJsonDataContractDeserializer.Instance.Parse<Movie>(clientJson);
2020

@@ -25,7 +25,8 @@ public void Can_serialize_a_movie()
2525
Console.WriteLine("CLIENT {0}\nSS {1}\nBCL {2}", clientJson, ssJson, wcfJson);
2626

2727
Assert.That(jsonModel, Is.EqualTo(bclJsonModel));
28-
}
28+
Assert.That(ssJson, Is.EqualTo(wcfJson));
29+
}
2930

3031
[Test]
3132
public void Can_deserialize_empty_type()

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public void Does_serialize_To_lowercase_underscore()
3939
var json = dto.ToJson();
4040

4141
Assert.That(json, Is.EqualTo(
42-
"{\"id\":1,\"imdb_id\":\"tt0111161\",\"title\":\"The Shawshank Redemption\",\"rating\":9.2,\"director\":\"Frank Darabont\",\"release_date\":\"\\/Date(792979200000)\\/\",\"tag_line\":\"Fear can hold you prisoner. Hope can set you free.\",\"genres\":[\"Crime\",\"Drama\"]}"));
42+
"{\"id\":1,\"title\":\"The Shawshank Redemption\",\"imdb_id\":\"tt0111161\",\"rating\":9.2,\"director\":\"Frank Darabont\",\"release_date\":\"\\/Date(792979200000)\\/\",\"tag_line\":\"Fear can hold you prisoner. Hope can set you free.\",\"genres\":[\"Crime\",\"Drama\"]}"));
4343

4444
Serialize(dto);
4545
}

tests/ServiceStack.Text.Tests/Support/MovieDtos.cs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,30 +27,30 @@ public Movie()
2727
this.Genres = new List<string>();
2828
}
2929

30-
[DataMember]
31-
[AutoIncrement]
30+
[DataMember(Order = 1, EmitDefaultValue = false, IsRequired = false)]
31+
[AutoIncrement]
3232
public int Id { get; set; }
3333

34-
[DataMember]
35-
public string ImdbId { get; set; }
34+
[DataMember(Order = 3, EmitDefaultValue = false, IsRequired = false)]
35+
public string ImdbId { get; set; }
3636

37-
[DataMember]
38-
public string Title { get; set; }
37+
[DataMember(Order = 2, EmitDefaultValue = false, IsRequired = false)]
38+
public string Title { get; set; }
3939

40-
[DataMember]
41-
public decimal Rating { get; set; }
40+
[DataMember(Order = 4, EmitDefaultValue = false, IsRequired = false)]
41+
public decimal Rating { get; set; }
4242

43-
[DataMember]
44-
public string Director { get; set; }
43+
[DataMember(Order = 5, EmitDefaultValue = false, IsRequired = false)]
44+
public string Director { get; set; }
4545

46-
[DataMember]
47-
public DateTime ReleaseDate { get; set; }
46+
[DataMember(Order = 6, EmitDefaultValue = false, IsRequired = false)]
47+
public DateTime ReleaseDate { get; set; }
4848

49-
[DataMember]
50-
public string TagLine { get; set; }
49+
[DataMember(Order = 7, EmitDefaultValue = false, IsRequired = false)]
50+
public string TagLine { get; set; }
5151

52-
[DataMember]
53-
public List<string> Genres { get; set; }
52+
[DataMember(Order = 8, EmitDefaultValue = false, IsRequired = false)]
53+
public List<string> Genres { get; set; }
5454

5555
#region AutoGen ReSharper code, only required by tests
5656
public bool Equals(Movie other)

0 commit comments

Comments
 (0)