diff --git a/src/ServiceStack.Text.MonoTouch/ServiceStack.Text.MonoTouch/ServiceStack.Text.MonoTouch.csproj b/src/ServiceStack.Text.MonoTouch/ServiceStack.Text.MonoTouch/ServiceStack.Text.MonoTouch.csproj
index 651fdcf5f..d25c38576 100644
--- a/src/ServiceStack.Text.MonoTouch/ServiceStack.Text.MonoTouch/ServiceStack.Text.MonoTouch.csproj
+++ b/src/ServiceStack.Text.MonoTouch/ServiceStack.Text.MonoTouch/ServiceStack.Text.MonoTouch.csproj
@@ -3,13 +3,15 @@
Debug
AnyCPU
- 10.0.0
+ 8.0.30703
2.0
{1137B5AC-2259-413C-A473-93721D2A7551}
- {6BC8ED88-2882-458C-8E55-DFD12B67127B};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
Library
ServiceStack.Text.MonoTouch
ServiceStack.Text.MonoTouch
+ Xamarin.iOS
+ v1.0
True
@@ -34,10 +36,9 @@
-
+
-
AssemblyUtils.cs
@@ -279,4 +280,5 @@
ServiceStack.Text.suo
+
\ No newline at end of file
diff --git a/src/ServiceStack.Text/Common/DeserializeCollection.cs b/src/ServiceStack.Text/Common/DeserializeCollection.cs
index ae63d932e..0f36b4790 100644
--- a/src/ServiceStack.Text/Common/DeserializeCollection.cs
+++ b/src/ServiceStack.Text/Common/DeserializeCollection.cs
@@ -54,12 +54,16 @@ public static ParseStringDelegate GetParseMethod(Type type)
public static ICollection ParseStringCollection(string value, Type createType)
{
+ if (value == null) return null;
+
var items = DeserializeArrayWithElements.ParseGenericArray(value, Serializer.ParseString);
return CollectionExtensions.CreateAndPopulate(createType, items);
}
public static ICollection ParseIntCollection(string value, Type createType)
{
+ if (value == null) return null;
+
var items = DeserializeArrayWithElements.ParseGenericArray(value, x => int.Parse(x));
return CollectionExtensions.CreateAndPopulate(createType, items);
}
diff --git a/src/ServiceStack.Text/Common/DeserializeDictionary.cs b/src/ServiceStack.Text/Common/DeserializeDictionary.cs
index afc453218..9cf716aa0 100644
--- a/src/ServiceStack.Text/Common/DeserializeDictionary.cs
+++ b/src/ServiceStack.Text/Common/DeserializeDictionary.cs
@@ -106,6 +106,8 @@ public static JsonObject ParseJsonObject(string value)
#if !SILVERLIGHT
public static Hashtable ParseHashtable(string value)
{
+ if (value == null) return null;
+
var index = VerifyAndGetStartIndex(value, typeof(Hashtable));
var result = new Hashtable();
@@ -174,6 +176,8 @@ public static IDictionary ParseIDictionary(string value, Type dictType)
public static Dictionary ParseStringDictionary(string value)
{
+ if (value == null) return null;
+
var index = VerifyAndGetStartIndex(value, typeof(Dictionary));
var result = new Dictionary();
diff --git a/src/ServiceStack.Text/Common/DeserializeListWithElements.cs b/src/ServiceStack.Text/Common/DeserializeListWithElements.cs
index 41d1b9677..8292d943d 100644
--- a/src/ServiceStack.Text/Common/DeserializeListWithElements.cs
+++ b/src/ServiceStack.Text/Common/DeserializeListWithElements.cs
@@ -141,7 +141,14 @@ public static ICollection ParseGenericList(string value, Type createListType,
do
{
var itemValue = Serializer.EatTypeValue(value, ref i);
- to.Add((T)parseFn(itemValue));
+ if (itemValue != null)
+ {
+ to.Add((T)parseFn(itemValue));
+ }
+ else
+ {
+ to.Add(default(T));
+ }
Serializer.EatWhitespace(value, ref i);
} while (++i < value.Length);
}
diff --git a/src/ServiceStack.Text/Common/DeserializeType.cs b/src/ServiceStack.Text/Common/DeserializeType.cs
index e23b25c61..e694a2b65 100644
--- a/src/ServiceStack.Text/Common/DeserializeType.cs
+++ b/src/ServiceStack.Text/Common/DeserializeType.cs
@@ -40,8 +40,8 @@ public static ParseStringDelegate GetParseMethod(TypeConfig typeConfig)
return value => ctorFn();
return typeof(TSerializer) == typeof(Json.JsonTypeSerializer)
- ? (ParseStringDelegate)(value => DeserializeTypeRefJson.StringToType(type, value, ctorFn, map))
- : value => DeserializeTypeRefJsv.StringToType(type, value, ctorFn, map);
+ ? (ParseStringDelegate)(value => DeserializeTypeRefJson.StringToType(typeConfig, value, ctorFn, map))
+ : value => DeserializeTypeRefJsv.StringToType(typeConfig, value, ctorFn, map);
}
public static object ObjectStringToType(string strType)
@@ -77,8 +77,17 @@ public static object ObjectStringToType(string strType)
public static Type ExtractType(string strType)
{
var typeAttrInObject = Serializer.TypeAttrInObject;
- if (strType != null
- && strType.Length > typeAttrInObject.Length
+ if (strType == null || strType.Length <= 1) return null;
+
+ var hasWhitespace = JsonUtils.WhiteSpaceChars.Contains(strType[1]);
+ if (hasWhitespace)
+ {
+ var pos = strType.IndexOf('"');
+ if (pos >= 0)
+ strType = "{" + strType.Substring(pos);
+ }
+
+ if (strType.Length > typeAttrInObject.Length
&& strType.Substring(0, typeAttrInObject.Length) == typeAttrInObject)
{
var propIndex = typeAttrInObject.Length;
diff --git a/src/ServiceStack.Text/Common/DeserializeTypeRefJson.cs b/src/ServiceStack.Text/Common/DeserializeTypeRefJson.cs
index 3068db8e3..534050ae2 100644
--- a/src/ServiceStack.Text/Common/DeserializeTypeRefJson.cs
+++ b/src/ServiceStack.Text/Common/DeserializeTypeRefJson.cs
@@ -52,12 +52,13 @@ internal static class DeserializeTypeRefJson
private static readonly JsonTypeSerializer Serializer = (JsonTypeSerializer)JsonTypeSerializer.Instance;
internal static object StringToType(
- Type type,
+ TypeConfig typeConfig,
string strType,
EmptyCtorDelegate ctorFn,
Dictionary typeAccessorMap)
{
var index = 0;
+ var type = typeConfig.Type;
if (strType == null)
return null;
@@ -90,37 +91,36 @@ internal static object StringToType(
var explicitTypeName = Serializer.ParseString(propertyValueStr);
var explicitType = AssemblyUtils.FindType(explicitTypeName);
- if (explicitType != null && !explicitType.IsInterface() && !explicitType.IsAbstract())
+ // let's do the type safety checks first before we even attempt to create
+ // a type instance
+ if (explicitType == null || explicitType.IsInterface() || explicitType.IsAbstract())
{
- instance = explicitType.CreateInstance();
+ Tracer.Instance.WriteWarning("Could not find type: " + propertyValueStr);
}
-
- if (instance == null)
+ else if (!type.IsAssignableFrom(explicitType))
{
- Tracer.Instance.WriteWarning("Could not find type: " + propertyValueStr);
+ Tracer.Instance.WriteWarning("Could not assign type: " + propertyValueStr);
}
else
{
- //If __type info doesn't match, ignore it.
- if (!type.InstanceOfType(instance))
- {
- instance = null;
- }
- else
+ instance = explicitType.CreateInstance();
+ }
+
+ if (instance != null)
+ {
+ var derivedType = instance.GetType();
+ if (derivedType != type)
{
- var derivedType = instance.GetType();
- if (derivedType != type)
+ var derivedTypeConfig = new TypeConfig(derivedType);
+ var map = DeserializeTypeRef.GetTypeAccessorMap(derivedTypeConfig, Serializer);
+ if (map != null)
{
- var derivedTypeConfig = new TypeConfig(derivedType);
- var map = DeserializeTypeRef.GetTypeAccessorMap(derivedTypeConfig, Serializer);
- if (map != null)
- {
- typeAccessorMap = map;
- }
+ typeAccessorMap = map;
}
}
}
+
Serializer.EatItemSeperatorOrMapEndChar(strType, ref index);
continue;
}
@@ -140,6 +140,10 @@ internal static object StringToType(
var parseFn = JsonReader.GetParseFn(propType);
var propertyValue = parseFn(propertyValueStr);
+ if (typeConfig.OnDeserializing != null)
+ {
+ propertyValue = typeConfig.OnDeserializing(instance, propertyName, propertyValue);
+ }
typeAccessor.SetProperty(instance, propertyValue);
}
@@ -157,8 +161,10 @@ internal static object StringToType(
}
catch (Exception e)
{
+ if (JsConfig.HasOnDeserializationErrorHandler) JsConfig.OnDeserializationError(instance, propType, propertyName, propertyValueStr, e);
if (JsConfig.ThrowOnDeserializationError) throw DeserializeTypeRef.GetSerializationException(propertyName, propertyValueStr, propType, e);
- else Tracer.Instance.WriteWarning("WARN: failed to set dynamic property {0} with: {1}", propertyName, propertyValueStr);
+
+ Tracer.Instance.WriteWarning("WARN: failed to set dynamic property {0} with: {1}", propertyName, propertyValueStr);
}
}
@@ -167,14 +173,25 @@ internal static object StringToType(
try
{
var propertyValue = typeAccessor.GetProperty(propertyValueStr);
+ if (typeConfig.OnDeserializing != null)
+ {
+ propertyValue = typeConfig.OnDeserializing(instance, propertyName, propertyValue);
+ }
typeAccessor.SetProperty(instance, propertyValue);
}
catch (Exception e)
{
+ if (JsConfig.HasOnDeserializationErrorHandler) JsConfig.OnDeserializationError(instance, typeAccessor.PropertyType, propertyName, propertyValueStr, e);
if (JsConfig.ThrowOnDeserializationError) throw DeserializeTypeRef.GetSerializationException(propertyName, propertyValueStr, typeAccessor.PropertyType, e);
- else Tracer.Instance.WriteWarning("WARN: failed to set property {0} with: {1}", propertyName, propertyValueStr);
+
+ Tracer.Instance.WriteWarning("WARN: failed to set property {0} with: {1}", propertyName, propertyValueStr);
}
}
+ else if (typeConfig.OnDeserializing != null)
+ {
+ // the property is not known by the DTO
+ typeConfig.OnDeserializing(instance, propertyName, propertyValueStr);
+ }
//Serializer.EatItemSeperatorOrMapEndChar(strType, ref index);
for (; index < strType.Length; index++) { var c = strType[index]; if (c >= JsonTypeSerializer.WhiteSpaceFlags.Length || !JsonTypeSerializer.WhiteSpaceFlags[c]) break; } //Whitespace inline
diff --git a/src/ServiceStack.Text/Common/DeserializeTypeRefJsv.cs b/src/ServiceStack.Text/Common/DeserializeTypeRefJsv.cs
index 9e9355455..058434897 100644
--- a/src/ServiceStack.Text/Common/DeserializeTypeRefJsv.cs
+++ b/src/ServiceStack.Text/Common/DeserializeTypeRefJsv.cs
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Runtime.Serialization;
-using System.Reflection;
using ServiceStack.Text.Json;
using ServiceStack.Text.Jsv;
@@ -12,12 +10,13 @@ internal static class DeserializeTypeRefJsv
private static readonly JsvTypeSerializer Serializer = (JsvTypeSerializer)JsvTypeSerializer.Instance;
internal static object StringToType(
- Type type,
+ TypeConfig typeConfig,
string strType,
EmptyCtorDelegate ctorFn,
Dictionary typeAccessorMap)
{
var index = 0;
+ var type = typeConfig.Type;
if (strType == null)
return null;
@@ -46,37 +45,33 @@ internal static object StringToType(
var explicitTypeName = Serializer.ParseString(propertyValueStr);
var explicitType = AssemblyUtils.FindType(explicitTypeName);
- if (explicitType != null && !explicitType.IsInterface() && !explicitType.IsAbstract())
+ if (explicitType == null || explicitType.IsInterface() || explicitType.IsAbstract())
{
- instance = explicitType.CreateInstance();
+ Tracer.Instance.WriteWarning("Could not find type: " + propertyValueStr);
}
-
- if (instance == null)
+ else if (!type.IsAssignableFrom(explicitType))
{
- Tracer.Instance.WriteWarning("Could not find type: " + propertyValueStr);
+ Tracer.Instance.WriteWarning("Could not assign type: " + propertyValueStr);
}
else
{
- //If __type info doesn't match, ignore it.
- if (!type.InstanceOfType(instance))
- {
- instance = null;
- }
- else
+ instance = explicitType.CreateInstance();
+ }
+
+ if (instance != null)
+ {
+ var derivedType = instance.GetType();
+ if (derivedType != type)
{
- var derivedType = instance.GetType();
- if (derivedType != type)
+ var derivedTypeConfig = new TypeConfig(derivedType);
+ var map = DeserializeTypeRef.GetTypeAccessorMap(derivedTypeConfig, Serializer);
+ if (map != null)
{
- var derivedTypeConfig = new TypeConfig(derivedType);
- var map = DeserializeTypeRef.GetTypeAccessorMap(derivedTypeConfig, Serializer);
- if (map != null)
- {
- typeAccessorMap = map;
- }
+ typeAccessorMap = map;
}
}
}
-
+
//Serializer.EatItemSeperatorOrMapEndChar(strType, ref index);
if (index != strType.Length) index++;
@@ -97,6 +92,10 @@ internal static object StringToType(
{
var parseFn = Serializer.GetParseFn(propType);
var propertyValue = parseFn(propertyValueStr);
+ if (typeConfig.OnDeserializing != null)
+ {
+ propertyValue = typeConfig.OnDeserializing(instance, propertyName, propertyValue);
+ }
typeAccessor.SetProperty(instance, propertyValue);
}
@@ -107,8 +106,10 @@ internal static object StringToType(
}
catch (Exception e)
{
+ if (JsConfig.HasOnDeserializationErrorHandler) JsConfig.OnDeserializationError(instance, propType, propertyName, propertyValueStr, e);
if (JsConfig.ThrowOnDeserializationError) throw DeserializeTypeRef.GetSerializationException(propertyName, propertyValueStr, propType, e);
- else Tracer.Instance.WriteWarning("WARN: failed to set dynamic property {0} with: {1}", propertyName, propertyValueStr);
+
+ Tracer.Instance.WriteWarning("WARN: failed to set dynamic property {0} with: {1}", propertyName, propertyValueStr);
}
}
@@ -117,14 +118,25 @@ internal static object StringToType(
try
{
var propertyValue = typeAccessor.GetProperty(propertyValueStr);
+ if (typeConfig.OnDeserializing != null)
+ {
+ propertyValue = typeConfig.OnDeserializing(instance, propertyName, propertyValue);
+ }
typeAccessor.SetProperty(instance, propertyValue);
}
catch (Exception e)
{
- if (JsConfig.ThrowOnDeserializationError) throw DeserializeTypeRef.GetSerializationException(propertyName, propertyValueStr, propType, e);
- else Tracer.Instance.WriteWarning("WARN: failed to set property {0} with: {1}", propertyName, propertyValueStr);
+ if (JsConfig.HasOnDeserializationErrorHandler) JsConfig.OnDeserializationError(instance, typeAccessor.PropertyType, propertyName, propertyValueStr, e);
+ if (JsConfig.ThrowOnDeserializationError) throw DeserializeTypeRef.GetSerializationException(propertyName, propertyValueStr, typeAccessor.PropertyType, e);
+
+ Tracer.Instance.WriteWarning("WARN: failed to set property {0} with: {1}", propertyName, propertyValueStr);
}
}
+ else if (typeConfig.OnDeserializing != null)
+ {
+ // the property is not known by the DTO
+ typeConfig.OnDeserializing(instance, propertyName, propertyValueStr);
+ }
//Serializer.EatItemSeperatorOrMapEndChar(strType, ref index);
if (index != strType.Length) index++;
diff --git a/src/ServiceStack.Text/Common/ITypeSerializer.cs b/src/ServiceStack.Text/Common/ITypeSerializer.cs
index f85e311d5..e8fda4c7a 100644
--- a/src/ServiceStack.Text/Common/ITypeSerializer.cs
+++ b/src/ServiceStack.Text/Common/ITypeSerializer.cs
@@ -7,6 +7,8 @@ namespace ServiceStack.Text.Common
internal interface ITypeSerializer
{
bool IncludeNullValues { get; }
+ bool IncludeNullValuesInDictionaries { get; }
+
string TypeAttrInObject { get; }
WriteObjectDelegate GetWriteFn();
diff --git a/src/ServiceStack.Text/Common/JsDelegates.cs b/src/ServiceStack.Text/Common/JsDelegates.cs
index d5595eb83..e28a8c777 100644
--- a/src/ServiceStack.Text/Common/JsDelegates.cs
+++ b/src/ServiceStack.Text/Common/JsDelegates.cs
@@ -33,4 +33,6 @@ namespace ServiceStack.Text.Common
public delegate object ConvertObjectDelegate(object fromObject);
public delegate object ConvertInstanceDelegate(object obj, Type type);
+
+ public delegate void DeserializationErrorDelegate(object instance, Type propertyType, string propertyName, string propertyValueStr, Exception e);
}
diff --git a/src/ServiceStack.Text/Common/WriteDictionary.cs b/src/ServiceStack.Text/Common/WriteDictionary.cs
index 852a5616c..4e8ed5283 100644
--- a/src/ServiceStack.Text/Common/WriteDictionary.cs
+++ b/src/ServiceStack.Text/Common/WriteDictionary.cs
@@ -110,7 +110,7 @@ public static void WriteIDictionary(TextWriter writer, object oMap)
var dictionaryValue = map[key];
var isNull = (dictionaryValue == null);
- if (isNull && !Serializer.IncludeNullValues) continue;
+ if (isNull && !Serializer.IncludeNullValuesInDictionaries) continue;
var keyType = key.GetType();
if (writeKeyFn == null || lastKeyType != keyType)
@@ -123,22 +123,34 @@ public static void WriteIDictionary(TextWriter writer, object oMap)
JsWriter.WriteItemSeperatorIfRanOnce(writer, ref ranOnce);
JsState.WritingKeyCount++;
- JsState.IsWritingValue = false;
-
- if (encodeMapKey)
+ try
{
- JsState.IsWritingValue = true; //prevent ""null""
- writer.Write(JsWriter.QuoteChar);
- writeKeyFn(writer, key);
- writer.Write(JsWriter.QuoteChar);
+ JsState.IsWritingValue = false;
+
+ if (encodeMapKey)
+ {
+ JsState.IsWritingValue = true; //prevent ""null""
+ try
+ {
+ writer.Write(JsWriter.QuoteChar);
+ writeKeyFn(writer, key);
+ writer.Write(JsWriter.QuoteChar);
+ }
+ finally
+ {
+ JsState.IsWritingValue = false;
+ }
+ }
+ else
+ {
+ writeKeyFn(writer, key);
+ }
}
- else
+ finally
{
- writeKeyFn(writer, key);
+ JsState.WritingKeyCount--;
}
- JsState.WritingKeyCount--;
-
writer.Write(JsWriter.MapKeySeperator);
if (isNull)
@@ -155,8 +167,14 @@ public static void WriteIDictionary(TextWriter writer, object oMap)
}
JsState.IsWritingValue = true;
- writeValueFn(writer, dictionaryValue);
- JsState.IsWritingValue = false;
+ try
+ {
+ writeValueFn(writer, dictionaryValue);
+ }
+ finally
+ {
+ JsState.IsWritingValue = false;
+ }
}
}
@@ -198,27 +216,32 @@ public static void WriteGenericIDictionary(
foreach (var kvp in map)
{
var isNull = (kvp.Value == null);
- if (isNull && !Serializer.IncludeNullValues) continue;
+ if (isNull && !Serializer.IncludeNullValuesInDictionaries) continue;
JsWriter.WriteItemSeperatorIfRanOnce(writer, ref ranOnce);
JsState.WritingKeyCount++;
- JsState.IsWritingValue = false;
-
- if (encodeMapKey)
+ try
{
- JsState.IsWritingValue = true; //prevent ""null""
- writer.Write(JsWriter.QuoteChar);
- writeKeyFn(writer, kvp.Key);
- writer.Write(JsWriter.QuoteChar);
+ JsState.IsWritingValue = false;
+
+ if (encodeMapKey)
+ {
+ JsState.IsWritingValue = true; //prevent ""null""
+ writer.Write(JsWriter.QuoteChar);
+ writeKeyFn(writer, kvp.Key);
+ writer.Write(JsWriter.QuoteChar);
+ }
+ else
+ {
+ writeKeyFn(writer, kvp.Key);
+ }
}
- else
+ finally
{
- writeKeyFn(writer, kvp.Key);
+ JsState.WritingKeyCount--;
}
- JsState.WritingKeyCount--;
-
writer.Write(JsWriter.MapKeySeperator);
if (isNull)
@@ -228,8 +251,14 @@ public static void WriteGenericIDictionary(
else
{
JsState.IsWritingValue = true;
- writeValueFn(writer, kvp.Value);
- JsState.IsWritingValue = false;
+ try
+ {
+ writeValueFn(writer, kvp.Value);
+ }
+ finally
+ {
+ JsState.IsWritingValue = false;
+ }
}
}
diff --git a/src/ServiceStack.Text/Common/WriteLists.cs b/src/ServiceStack.Text/Common/WriteLists.cs
index a282b6590..0699f2201 100644
--- a/src/ServiceStack.Text/Common/WriteLists.cs
+++ b/src/ServiceStack.Text/Common/WriteLists.cs
@@ -17,6 +17,7 @@
using System.Reflection;
using System.Threading;
using System.Linq;
+using ServiceStack.Text.Json;
namespace ServiceStack.Text.Common
{
@@ -179,10 +180,26 @@ public static void WriteIEnumerable(TextWriter writer, object oValueCollection)
var valueCollection = (IEnumerable)oValueCollection;
var ranOnce = false;
+ Type lastType = null;
foreach (var valueItem in valueCollection)
{
- if (toStringFn == null)
- toStringFn = Serializer.GetWriteFn(valueItem.GetType());
+ if ((toStringFn == null) || (valueItem != null && valueItem.GetType() != lastType))
+ {
+ if (valueItem != null)
+ {
+ if (valueItem.GetType() != lastType)
+ {
+ lastType = valueItem.GetType();
+ toStringFn = Serializer.GetWriteFn(lastType);
+ }
+ }
+ else
+ {
+ // this can happen if the first item in the collection was null
+ lastType = typeof (object);
+ toStringFn = Serializer.GetWriteFn(lastType);
+ }
+ }
JsWriter.WriteItemSeperatorIfRanOnce(writer, ref ranOnce);
diff --git a/src/ServiceStack.Text/Common/WriteType.cs b/src/ServiceStack.Text/Common/WriteType.cs
index 721e08d54..b597cc9b0 100644
--- a/src/ServiceStack.Text/Common/WriteType.cs
+++ b/src/ServiceStack.Text/Common/WriteType.cs
@@ -108,6 +108,16 @@ static Func GetShouldSerializeMethod(MemberInfo member)
null, Type.EmptyTypes, null);
return (method == null || method.ReturnType != typeof(bool)) ? null : (Func)Delegate.CreateDelegate(typeof(Func), method);
}
+
+ static Func ShouldSerialize(Type type)
+ {
+ var method = type.GetMethod("ShouldSerialize", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(string) }, null);
+ return (method == null || method.ReturnType != typeof(bool?))
+ ? null
+ : (Func)Delegate.CreateDelegate(typeof(Func), method);
+ }
+
+
private static bool Init()
{
if (!typeof(T).IsClass() && !typeof(T).IsInterface() && !JsConfig.TreatAsRefType(typeof(T))) return false;
@@ -124,6 +134,8 @@ private static bool Init()
return typeof(T).IsDto();
}
+ var shouldSerializeByName = ShouldSerialize(typeof(T));
+
// NOTE: very limited support for DataContractSerialization (DCS)
// NOT supporting Serializable
// support for DCS is intended for (re)Name of properties and Ignore by NOT having a DataMember present
@@ -174,7 +186,8 @@ private static bool Init()
propertyInfo.GetValueGetter(),
Serializer.GetWriteFn(propertyType),
propertyType.GetDefaultValue(),
- shouldSerialize
+ shouldSerialize,
+ shouldSerializeByName
);
}
@@ -221,7 +234,8 @@ private static bool Init()
fieldInfo.GetValueGetter(),
Serializer.GetWriteFn(propertyType),
defaultValue,
- shouldSerialize
+ shouldSerialize,
+ shouldSerializeByName
);
}
PropertyWriters = PropertyWriters.OrderBy(x => x.propertyOrder).ToArray();
@@ -253,9 +267,10 @@ internal string PropertyName
internal readonly WriteObjectDelegate WriteFn;
internal readonly object DefaultValue;
internal readonly Func shouldSerialize;
+ internal readonly Func shouldSerializeByName;
public TypePropertyWriter(string propertyName, string propertyReflectedName, string propertyNameCLSFriendly, string propertyNameLowercaseUnderscore, int propertyOrder, bool propertySuppressDefaultConfig,bool propertySuppressDefaultAttribute,
- Func getterFn, WriteObjectDelegate writeFn, object defaultValue, Func shouldSerialize)
+ Func getterFn, WriteObjectDelegate writeFn, object defaultValue, Func shouldSerialize, Func shouldSerializeByName)
{
this.propertyName = propertyName;
this.propertyOrder = propertyOrder;
@@ -269,6 +284,7 @@ public TypePropertyWriter(string propertyName, string propertyReflectedName, str
this.WriteFn = writeFn;
this.DefaultValue = defaultValue;
this.shouldSerialize = shouldSerialize;
+ this.shouldSerializeByName = shouldSerializeByName;
}
}
@@ -332,26 +348,46 @@ public static void WriteProperties(TextWriter writer, object value)
var len = PropertyWriters.Length;
var exclude = JsConfig.ExcludePropertyReferences ?? new string[0];
ConvertToUpper(exclude);
+
+ var hasValueNotNull = (value != null);
+
for (int index = 0; index < len; index++)
{
var propertyWriter = PropertyWriters[index];
-
- if (propertyWriter.shouldSerialize != null && !propertyWriter.shouldSerialize((T)value)) continue;
-
- var propertyValue = value != null
- ? propertyWriter.GetterFn((T)value)
- : null;
- if (propertyWriter.propertySuppressDefaultAttribute && Equals(propertyWriter.DefaultValue, propertyValue))
+ if (hasValueNotNull && propertyWriter.shouldSerialize != null && !propertyWriter.shouldSerialize((T)value)) continue;
+ bool dontSkipDefault=false;
+ if (hasValueNotNull && propertyWriter.shouldSerializeByName != null)
{
- continue;
+ var shouldSerialize = propertyWriter.shouldSerializeByName((T) value, propertyWriter.PropertyName);
+ if (shouldSerialize.HasValue)
+ {
+ if (shouldSerialize.Value)
+ {
+ dontSkipDefault = true;
+ }
+ else
+ {
+ continue;
+ }
+ }
}
- if ((propertyValue == null
- || (propertyWriter.propertySuppressDefaultConfig && Equals(propertyWriter.DefaultValue, propertyValue)))
- && !Serializer.IncludeNullValues
- )
+ var propertyValue = hasValueNotNull
+ ? propertyWriter.GetterFn((T)value)
+ : null;
+ if (!dontSkipDefault)
{
- continue;
+ if (propertyWriter.propertySuppressDefaultAttribute && Equals(propertyWriter.DefaultValue, propertyValue))
+ {
+ continue;
+ }
+ if ((propertyValue == null
+ || (propertyWriter.propertySuppressDefaultConfig && Equals(propertyWriter.DefaultValue, propertyValue)))
+ && !Serializer.IncludeNullValues
+ )
+ {
+ continue;
+ }
}
if (exclude.Any() && exclude.Contains(propertyWriter.propertyCombinedNameUpper)) continue;
@@ -363,15 +399,21 @@ public static void WriteProperties(TextWriter writer, object value)
writer.Write(JsWriter.MapKeySeperator);
if (typeof(TSerializer) == typeof(JsonTypeSerializer)) JsState.IsWritingValue = true;
- if (propertyValue == null)
+ try
{
- writer.Write(JsonUtils.Null);
+ if (propertyValue == null)
+ {
+ writer.Write(JsonUtils.Null);
+ }
+ else
+ {
+ propertyWriter.WriteFn(writer, propertyValue);
+ }
}
- else
+ finally
{
- propertyWriter.WriteFn(writer, propertyValue);
+ if (typeof(TSerializer) == typeof(JsonTypeSerializer)) JsState.IsWritingValue = false;
}
- if (typeof(TSerializer) == typeof(JsonTypeSerializer)) JsState.IsWritingValue = false;
}
}
diff --git a/src/ServiceStack.Text/JsConfig.cs b/src/ServiceStack.Text/JsConfig.cs
index b409e3a78..9b548f43b 100644
--- a/src/ServiceStack.Text/JsConfig.cs
+++ b/src/ServiceStack.Text/JsConfig.cs
@@ -142,6 +142,21 @@ public static bool IncludeNullValues
}
}
+ private static bool? sIncludeNullValuesInDictionaries;
+ public static bool IncludeNullValuesInDictionaries
+ {
+ get
+ {
+ return (JsConfigScope.Current != null ? JsConfigScope.Current.IncludeNullValuesInDictionaries : null)
+ ?? sIncludeNullValuesInDictionaries
+ ?? false;
+ }
+ set
+ {
+ if (!sIncludeNullValuesInDictionaries.HasValue) sIncludeNullValuesInDictionaries = value;
+ }
+ }
+
private static bool? sTreatEnumAsInteger;
public static bool TreatEnumAsInteger
{
@@ -386,6 +401,27 @@ public static bool ThrowOnDeserializationError
}
}
+ ///
+ /// Gets or sets a value indicating if the framework should call an error handler when
+ /// an exception happens during the deserialization.
+ ///
+ /// Parameters have following meaning in order: deserialized entity, property name, parsed value, property type, caught exception.
+ private static DeserializationErrorDelegate sOnDeserializationError;
+ public static DeserializationErrorDelegate OnDeserializationError
+ {
+ get
+ {
+ return (JsConfigScope.Current != null ? JsConfigScope.Current.OnDeserializationError : null)
+ ?? sOnDeserializationError;
+ }
+ set { sOnDeserializationError = value; }
+ }
+
+ ///
+ /// Gets whether a deserialization error handler is configured or not.
+ ///
+ public static bool HasOnDeserializationErrorHandler { get { return OnDeserializationError != null; } }
+
///
/// Gets or sets a value indicating if the framework should always convert to UTC format instead of local time.
///
@@ -570,6 +606,7 @@ public static void Reset()
sTryToParseNumericType = null;
sConvertObjectTypesIntoStringDictionary = null;
sIncludeNullValues = null;
+ sIncludeNullValuesInDictionaries = null;
sExcludeTypeInfo = null;
sEmitCamelCaseNames = null;
sEmitLowercaseUnderscoreNames = null;
@@ -592,6 +629,7 @@ public static void Reset()
TreatValueAsRefTypes = new HashSet { typeof(KeyValuePair<,>) };
PropertyConvention = JsonPropertyConvention.ExactMatch;
sExcludePropertyReferences = null;
+ sOnDeserializationError = null;
}
public static void Reset(Type cachesForType)
@@ -615,11 +653,11 @@ internal static void InvokeReset(this Type genericType)
/// Provide hint to MonoTouch AOT compiler to pre-compile generic classes for all your DTOs.
/// Just needs to be called once in a static constructor.
///
- [MonoTouch.Foundation.Preserve]
+ [Foundation.Preserve]
public static void InitForAot() {
}
- [MonoTouch.Foundation.Preserve]
+ [Foundation.Preserve]
public static void RegisterForAot()
{
RegisterTypeForAot();
@@ -668,20 +706,20 @@ public static void RegisterForAot()
RegisterTypeForAot();
}
- [MonoTouch.Foundation.Preserve]
+ [Foundation.Preserve]
public static void RegisterTypeForAot()
{
AotConfig.RegisterSerializers();
}
- [MonoTouch.Foundation.Preserve]
+ [Foundation.Preserve]
static void RegisterQueryStringWriter()
{
var i = 0;
if (QueryStringWriter.WriteFn() != null) i++;
}
- [MonoTouch.Foundation.Preserve]
+ [Foundation.Preserve]
internal static int RegisterElement()
{
var i = 0;
@@ -694,7 +732,7 @@ internal static int RegisterElement()
///
/// Class contains Ahead-of-Time (AOT) explicit class declarations which is used only to workaround "-aot-only" exceptions occured on device only.
///
- [MonoTouch.Foundation.Preserve(AllMembers=true)]
+ [Foundation.Preserve(AllMembers=true)]
internal class AotConfig
{
internal static JsReader jsonReader;
@@ -829,7 +867,7 @@ internal static void RegisterElement() where TSerializ
}
#if MONOTOUCH
- [MonoTouch.Foundation.Preserve(AllMembers=true)]
+ [Foundation.Preserve(AllMembers=true)]
internal class Poco
{
public string Dummy { get; set; }
@@ -961,6 +999,18 @@ public static Func OnDeserializedFn
set { onDeserializedFn = value; }
}
+ public static bool HasDeserialingFn
+ {
+ get { return OnDeserializingFn != null; }
+ }
+
+ private static Func onDeserializingFn;
+ public static Func OnDeserializingFn
+ {
+ get { return onDeserializingFn; }
+ set { onDeserializingFn = value; }
+ }
+
///
/// Exclude specific properties of this type from being serialized
///
diff --git a/src/ServiceStack.Text/JsConfigScope.cs b/src/ServiceStack.Text/JsConfigScope.cs
index 7a9cca8f8..45362217e 100644
--- a/src/ServiceStack.Text/JsConfigScope.cs
+++ b/src/ServiceStack.Text/JsConfigScope.cs
@@ -4,6 +4,7 @@
using System.Text;
using System.Threading;
using System.Diagnostics;
+using ServiceStack.Text.Common;
namespace ServiceStack.Text
{
@@ -59,6 +60,7 @@ public void Dispose()
public bool? TryToParsePrimitiveTypeValues { get; set; }
public bool? TryToParseNumericType { get; set; }
public bool? IncludeNullValues { get; set; }
+ public bool? IncludeNullValuesInDictionaries { get; set; }
public bool? TreatEnumAsInteger { get; set; }
public bool? ExcludeTypeInfo { get; set; }
public bool? IncludeTypeInfo { get; set; }
@@ -81,5 +83,6 @@ public void Dispose()
public int? MaxDepth { get; set; }
public EmptyCtorFactoryDelegate ModelFactory { get; set; }
public string[] ExcludePropertyReferences { get; set; }
+ public DeserializationErrorDelegate OnDeserializationError { get; set; }
}
}
diff --git a/src/ServiceStack.Text/Json/JsonTypeSerializer.cs b/src/ServiceStack.Text/Json/JsonTypeSerializer.cs
index fb3809713..b447ccf00 100644
--- a/src/ServiceStack.Text/Json/JsonTypeSerializer.cs
+++ b/src/ServiceStack.Text/Json/JsonTypeSerializer.cs
@@ -28,6 +28,11 @@ public bool IncludeNullValues
get { return JsConfig.IncludeNullValues; }
}
+ public bool IncludeNullValuesInDictionaries
+ {
+ get { return JsConfig.IncludeNullValuesInDictionaries; }
+ }
+
public string TypeAttrInObject
{
get { return JsConfig.JsonTypeAttrInObject; }
@@ -42,10 +47,10 @@ internal static string GetTypeAttrInObject(string typeAttr)
static JsonTypeSerializer()
{
- WhiteSpaceFlags[' '] = true;
- WhiteSpaceFlags['\t'] = true;
- WhiteSpaceFlags['\r'] = true;
- WhiteSpaceFlags['\n'] = true;
+ foreach (var c in JsonUtils.WhiteSpaceChars)
+ {
+ WhiteSpaceFlags[c] = true;
+ }
}
public WriteObjectDelegate GetWriteFn()
diff --git a/src/ServiceStack.Text/Json/JsonUtils.cs b/src/ServiceStack.Text/Json/JsonUtils.cs
index 442b88b12..5197682da 100644
--- a/src/ServiceStack.Text/Json/JsonUtils.cs
+++ b/src/ServiceStack.Text/Json/JsonUtils.cs
@@ -11,6 +11,8 @@ public static class JsonUtils
public const string True = "true";
public const string False = "false";
+ public static char[] WhiteSpaceChars = new[] { ' ', '\t', '\r', '\n' };
+
static readonly char[] EscapeChars = new[]
{
QuoteChar, '\n', '\r', '\t', '"', '\\', '\f', '\b',
diff --git a/src/ServiceStack.Text/JsonObject.cs b/src/ServiceStack.Text/JsonObject.cs
index 50e289098..8597ad0da 100644
--- a/src/ServiceStack.Text/JsonObject.cs
+++ b/src/ServiceStack.Text/JsonObject.cs
@@ -117,9 +117,9 @@ public string Child(string key)
return base[key];
}
#if !SILVERLIGHT && !MONOTOUCH
- static readonly Regex NumberRegEx = new Regex(@"^[0-9]*(?:\.[0-9]*)?$", RegexOptions.Compiled);
+ static readonly Regex NumberRegEx = new Regex(@"^(0|[1-9]*)(?:\.[0-9]*)?$", RegexOptions.Compiled);
#else
- static readonly Regex NumberRegEx = new Regex(@"^[0-9]*(?:\.[0-9]*)?$");
+ static readonly Regex NumberRegEx = new Regex(@"^(0|[1-9]*)(?:\.[0-9]*)?$");
#endif
///
/// Write JSON Array, Object, bool or number values as raw string
diff --git a/src/ServiceStack.Text/Jsv/JsvTypeSerializer.cs b/src/ServiceStack.Text/Jsv/JsvTypeSerializer.cs
index 71ef18e7e..54389fd97 100644
--- a/src/ServiceStack.Text/Jsv/JsvTypeSerializer.cs
+++ b/src/ServiceStack.Text/Jsv/JsvTypeSerializer.cs
@@ -28,6 +28,11 @@ public bool IncludeNullValues
get { return false; } //Doesn't support null values, treated as "null" string literal
}
+ public bool IncludeNullValuesInDictionaries
+ {
+ get { return false; } //Doesn't support null values, treated as "null" string literal
+ }
+
public string TypeAttrInObject
{
get { return JsConfig.JsvTypeAttrInObject; }
diff --git a/src/ServiceStack.Text/Marc/TypeAccessor.cs b/src/ServiceStack.Text/Marc/TypeAccessor.cs
index 403ee3920..a5922ca39 100644
--- a/src/ServiceStack.Text/Marc/TypeAccessor.cs
+++ b/src/ServiceStack.Text/Marc/TypeAccessor.cs
@@ -1,7 +1,9 @@
-using System;
+using System;
using System.Collections;
using System.Reflection;
+#if !SILVERLIGHT && !MONOTOUCH && !XBOX
using System.Reflection.Emit;
+#endif
using System.Threading;
//using System.Dynamic;
diff --git a/src/ServiceStack.Text/QueryStringSerializer.cs b/src/ServiceStack.Text/QueryStringSerializer.cs
index 7cbecfb6e..cdef40fe9 100644
--- a/src/ServiceStack.Text/QueryStringSerializer.cs
+++ b/src/ServiceStack.Text/QueryStringSerializer.cs
@@ -168,17 +168,28 @@ public static void WriteIDictionary(TextWriter writer, object oMap)
ranOnce = true;
JsState.WritingKeyCount++;
- JsState.IsWritingValue = false;
-
- writeKeyFn(writer, key);
+ try
+ {
+ JsState.IsWritingValue = false;
- JsState.WritingKeyCount--;
+ writeKeyFn(writer, key);
+ }
+ finally
+ {
+ JsState.WritingKeyCount--;
+ }
writer.Write("=");
JsState.IsWritingValue = true;
- writeValueFn(writer, dictionaryValue);
- JsState.IsWritingValue = false;
+ try
+ {
+ writeValueFn(writer, dictionaryValue);
+ }
+ finally
+ {
+ JsState.IsWritingValue = false;
+ }
}
}
finally
diff --git a/src/ServiceStack.Text/ReflectionExtensions.cs b/src/ServiceStack.Text/ReflectionExtensions.cs
index 95ed990f6..98f644439 100644
--- a/src/ServiceStack.Text/ReflectionExtensions.cs
+++ b/src/ServiceStack.Text/ReflectionExtensions.cs
@@ -536,6 +536,16 @@ public static PropertyInfo[] GetSerializableProperties(this Type type)
return publicReadableProperties.Where(prop => !prop.CustomAttributes(false).Any(attr => attr.GetType().Name == IgnoreDataMember)).ToArray();
}
+ public static Func GetOnDeserializing()
+ {
+ var method = typeof(T).GetMethod("OnDeserializing", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { typeof(string), typeof(object) }, null);
+ if (method == null || method.ReturnType != typeof (object))
+ {
+ return null;
+ }
+ return (Func)Delegate.CreateDelegate(typeof(Func), method);
+ }
+
public static FieldInfo[] GetSerializableFields(this Type type)
{
if (type.IsDto()) {
diff --git a/src/ServiceStack.Text/TypeConfig.cs b/src/ServiceStack.Text/TypeConfig.cs
index bb8c1a020..8aff70ce4 100644
--- a/src/ServiceStack.Text/TypeConfig.cs
+++ b/src/ServiceStack.Text/TypeConfig.cs
@@ -10,6 +10,7 @@ internal class TypeConfig
internal bool EnableAnonymousFieldSetterses;
internal PropertyInfo[] Properties;
internal FieldInfo[] Fields;
+ internal Func