2
// http://code.google.com/p/servicestack/wiki/TypeSerializer
3
// ServiceStack.Text: .NET C# POCO Type Text Serializer.
6
// Demis Bellot (demis.bellot@gmail.com)
8
// Copyright 2011 Liquidbit Ltd.
10
// Licensed under the same terms of ServiceStack: new BSD license.
13
#if !XBOX && !MONOTOUCH && !SILVERLIGHT
14
using System.Reflection.Emit;
18
using System.Collections.Generic;
19
using System.Reflection;
20
using System.Runtime.Serialization;
23
namespace ServiceStack.Text.Common
25
internal static class DeserializeType<TSerializer>
26
where TSerializer : ITypeSerializer
28
private static readonly ITypeSerializer Serializer = JsWriter.GetTypeSerializer<TSerializer>();
30
private static readonly string TypeAttrInObject = Serializer.TypeAttrInObject;
32
public static ParseStringDelegate GetParseMethod(TypeConfig typeConfig)
34
var type = typeConfig.Type;
36
if (!type.IsClass || type.IsAbstract || type.IsInterface) return null;
38
var propertyInfos = type.GetSerializableProperties();
39
if (propertyInfos.Length == 0)
41
var emptyCtorFn = ReflectionExtensions.GetConstructorMethodToCache(type);
42
return value => emptyCtorFn();
45
var map = new Dictionary<string, TypeAccessor>(StringComparer.OrdinalIgnoreCase);
47
var isDataContract = type.GetCustomAttributes(typeof(DataContractAttribute), false).Any();
49
foreach (var propertyInfo in propertyInfos)
51
var propertyName = propertyInfo.Name;
54
var dcsDataMember = propertyInfo.GetCustomAttributes(typeof(DataMemberAttribute), false).FirstOrDefault() as DataMemberAttribute;
55
if (dcsDataMember != null && dcsDataMember.Name != null)
57
propertyName = dcsDataMember.Name;
60
map[propertyName] = TypeAccessor.Create(Serializer, typeConfig, propertyInfo);
63
var ctorFn = ReflectionExtensions.GetConstructorMethodToCache(type);
65
return typeof(TSerializer) == typeof(Json.JsonTypeSerializer)
66
? (ParseStringDelegate)(value => DeserializeTypeRefJson.StringToType(type, value, ctorFn, map))
67
: value => DeserializeTypeRefJsv.StringToType(type, value, ctorFn, map);
70
public static object ObjectStringToType(string strType)
72
var type = ExtractType(strType);
75
var parseFn = Serializer.GetParseFn(type);
76
var propertyValue = parseFn(strType);
83
public static Type ExtractType(string strType)
86
&& strType.Length > TypeAttrInObject.Length
87
&& strType.Substring(0, TypeAttrInObject.Length) == TypeAttrInObject)
89
var propIndex = TypeAttrInObject.Length;
90
var typeName = Serializer.EatValue(strType, ref propIndex);
91
var type = AssemblyUtils.FindType(typeName);
94
Tracer.Instance.WriteWarning("Could not find type: " + typeName);
101
public static object ParseAbstractType<T>(string value)
103
if (typeof(T).IsAbstract)
105
if (string.IsNullOrEmpty(value)) return null;
106
var concreteType = ExtractType(value);
107
if (concreteType != null)
109
return Serializer.GetParseFn(concreteType)(value);
111
Tracer.Instance.WriteWarning(
112
"Could not deserialize Abstract Type with unknown concrete type: " + typeof(T).FullName);
119
internal class TypeAccessor
121
internal ParseStringDelegate GetProperty;
122
internal SetPropertyDelegate SetProperty;
124
public static Type ExtractType(ITypeSerializer Serializer, string strType)
126
var TypeAttrInObject = Serializer.TypeAttrInObject;
129
&& strType.Length > TypeAttrInObject.Length
130
&& strType.Substring(0, TypeAttrInObject.Length) == TypeAttrInObject)
132
var propIndex = TypeAttrInObject.Length;
133
var typeName = Serializer.EatValue(strType, ref propIndex);
134
var type = AssemblyUtils.FindType(typeName);
137
Tracer.Instance.WriteWarning("Could not find type: " + typeName);
144
public static TypeAccessor Create(ITypeSerializer serializer, TypeConfig typeConfig, PropertyInfo propertyInfo)
146
return new TypeAccessor {
147
GetProperty = serializer.GetParseFn(propertyInfo.PropertyType),
148
SetProperty = GetSetPropertyMethod(typeConfig, propertyInfo),
152
private static SetPropertyDelegate GetSetPropertyMethod(TypeConfig typeConfig, PropertyInfo propertyInfo)
154
if (!propertyInfo.CanWrite && !typeConfig.EnableAnonymousFieldSetterses) return null;
156
FieldInfo fieldInfo = null;
157
if (!propertyInfo.CanWrite)
159
//TODO: What string comparison is used in SST?
160
var fieldName = string.Format("<{0}>i__Field", propertyInfo.Name);
161
var fieldInfos = typeConfig.Type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField);
162
foreach (var f in fieldInfos)
164
if (f.IsInitOnly && f.FieldType == propertyInfo.PropertyType && f.Name == fieldName)
171
if (fieldInfo == null) return null;
174
#if SILVERLIGHT || MONOTOUCH || XBOX
175
if (propertyInfo.CanWrite)
177
var setMethodInfo = propertyInfo.GetSetMethod(true);
178
return (instance, value) => setMethodInfo.Invoke(instance, new[] { value });
180
if (fieldInfo == null) return null;
181
return (instance, value) => fieldInfo.SetValue(instance, value);
183
return propertyInfo.CanWrite
184
? CreateIlPropertySetter(propertyInfo)
185
: CreateIlFieldSetter(fieldInfo);
189
#if !SILVERLIGHT && !MONOTOUCH && !XBOX
191
private static SetPropertyDelegate CreateIlPropertySetter(PropertyInfo propertyInfo)
193
var propSetMethod = propertyInfo.GetSetMethod(true);
194
if (propSetMethod == null)
197
var setter = CreateDynamicSetMethod(propertyInfo);
199
var generator = setter.GetILGenerator();
200
generator.Emit(OpCodes.Ldarg_0);
201
generator.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
202
generator.Emit(OpCodes.Ldarg_1);
204
generator.Emit(propertyInfo.PropertyType.IsClass
207
propertyInfo.PropertyType);
209
generator.EmitCall(OpCodes.Callvirt, propSetMethod, (Type[])null);
210
generator.Emit(OpCodes.Ret);
212
return (SetPropertyDelegate)setter.CreateDelegate(typeof(SetPropertyDelegate));
215
private static SetPropertyDelegate CreateIlFieldSetter(FieldInfo fieldInfo)
217
var setter = CreateDynamicSetMethod(fieldInfo);
219
var generator = setter.GetILGenerator();
220
generator.Emit(OpCodes.Ldarg_0);
221
generator.Emit(OpCodes.Castclass, fieldInfo.DeclaringType);
222
generator.Emit(OpCodes.Ldarg_1);
224
generator.Emit(fieldInfo.FieldType.IsClass
227
fieldInfo.FieldType);
229
generator.Emit(OpCodes.Stfld, fieldInfo);
230
generator.Emit(OpCodes.Ret);
232
return (SetPropertyDelegate)setter.CreateDelegate(typeof(SetPropertyDelegate));
235
private static DynamicMethod CreateDynamicSetMethod(MemberInfo memberInfo)
237
var args = new[] { typeof(object), typeof(object) };
238
var name = string.Format("_{0}{1}_", "Set", memberInfo.Name);
239
var returnType = typeof(void);
241
return !memberInfo.DeclaringType.IsInterface
242
? new DynamicMethod(name, returnType, args, memberInfo.DeclaringType, true)
243
: new DynamicMethod(name, returnType, args, memberInfo.Module, true);
247
internal static SetPropertyDelegate GetSetPropertyMethod(Type type, PropertyInfo propertyInfo)
249
if (!propertyInfo.CanWrite) return null;
251
#if SILVERLIGHT || MONOTOUCH || XBOX
252
var setMethodInfo = propertyInfo.GetSetMethod(true);
253
return (instance, value) => setMethodInfo.Invoke(instance, new[] { value });
255
return CreateIlPropertySetter(propertyInfo);