Skip to content

Instantly share code, notes, and snippets.

@pauldbau
Created September 20, 2012 23:21
Show Gist options
  • Save pauldbau/3758928 to your computer and use it in GitHub Desktop.
Save pauldbau/3758928 to your computer and use it in GitHub Desktop.
ServiceStack/MonoTouch: Fixes to JsonAotConfig for various AOT issues in MonoTouch
using System;
using System.Collections.Generic;
using System.IO;
using ServiceStack.Text.Common;
using ServiceStack.Text.Json;
using ServiceStack.Text.Jsv;
#if WINDOWS_PHONE
using ServiceStack.Text.WP;
#endif
namespace ServiceStack.Text
{
public static class
JsConfig
{
static JsConfig()
{
//In-built default serialization, to Deserialize Color struct do:
//JsConfig<System.Drawing.Color>.SerializeFn = c => c.ToString().Replace("Color ", "").Replace("[", "").Replace("]", "");
//JsConfig<System.Drawing.Color>.DeSerializeFn = System.Drawing.Color.FromName;
Reset();
}
[ThreadStatic]
private static bool? tsConvertObjectTypesIntoStringDictionary;
private static bool? sConvertObjectTypesIntoStringDictionary;
public static bool ConvertObjectTypesIntoStringDictionary
{
get
{
return tsConvertObjectTypesIntoStringDictionary ?? sConvertObjectTypesIntoStringDictionary ?? false;
}
set
{
tsConvertObjectTypesIntoStringDictionary = value;
if (!sConvertObjectTypesIntoStringDictionary.HasValue) sConvertObjectTypesIntoStringDictionary = value;
}
}
[ThreadStatic]
private static bool? tsIncludeNullValues;
private static bool? sIncludeNullValues;
public static bool IncludeNullValues
{
get
{
return tsIncludeNullValues ?? sIncludeNullValues ?? false;
}
set
{
tsIncludeNullValues = value;
if (!sIncludeNullValues.HasValue) sIncludeNullValues = value;
}
}
[ThreadStatic]
private static bool? tsExcludeTypeInfo;
private static bool? sExcludeTypeInfo;
public static bool ExcludeTypeInfo
{
get
{
return tsExcludeTypeInfo ?? sExcludeTypeInfo ?? false;
}
set
{
tsExcludeTypeInfo = value;
if (!sExcludeTypeInfo.HasValue) sExcludeTypeInfo = value;
}
}
[ThreadStatic]
private static JsonDateHandler? tsDateHandler;
private static JsonDateHandler? sDateHandler;
public static JsonDateHandler DateHandler
{
get
{
return tsDateHandler ?? sDateHandler ?? JsonDateHandler.TimestampOffset;
}
set
{
tsDateHandler = value;
if (!sDateHandler.HasValue) sDateHandler = value;
}
}
/// <summary>
/// <see langword="true"/> if the <see cref="ITypeSerializer"/> is configured
/// to take advantage of <see cref="CLSCompliantAttribute"/> specification,
/// to support user-friendly serialized formats, ie emitting camelCasing for JSON
/// and parsing member names and enum values in a case-insensitive manner.
/// </summary>
[ThreadStatic]
private static bool? tsEmitCamelCaseNames;
private static bool? sEmitCamelCaseNames;
public static bool EmitCamelCaseNames
{
// obeying the use of ThreadStatic, but allowing for setting JsConfig once as is the normal case
get
{
return tsEmitCamelCaseNames ?? sEmitCamelCaseNames ?? false;
}
set
{
tsEmitCamelCaseNames = value;
if (!sEmitCamelCaseNames.HasValue) sEmitCamelCaseNames = value;
}
}
/// <summary>
/// Gets or sets a value indicating if the framework should throw serialization exceptions
/// or continue regardless of deserialization errors. If <see langword="true"/> the framework
/// will throw; otherwise, it will parse as many fields as possible. The default is <see langword="false"/>.
/// </summary>
[ThreadStatic]
private static bool? tsThrowOnDeserializationError;
private static bool? sThrowOnDeserializationError;
public static bool ThrowOnDeserializationError
{
// obeying the use of ThreadStatic, but allowing for setting JsConfig once as is the normal case
get
{
return tsThrowOnDeserializationError ?? sThrowOnDeserializationError ?? false;
}
set
{
tsThrowOnDeserializationError = value;
if (!sThrowOnDeserializationError.HasValue) sThrowOnDeserializationError = value;
}
}
internal static HashSet<Type> HasSerializeFn = new HashSet<Type>();
internal static HashSet<Type> TreatValueAsRefTypes = new HashSet<Type>();
internal static bool TreatAsRefType(Type valueType)
{
return TreatValueAsRefTypes.Contains(valueType.IsGenericType ? valueType.GetGenericTypeDefinition() : valueType);
}
public static void Reset()
{
tsConvertObjectTypesIntoStringDictionary = sConvertObjectTypesIntoStringDictionary = null;
tsIncludeNullValues = sIncludeNullValues = null;
tsExcludeTypeInfo = sExcludeTypeInfo = null;
tsEmitCamelCaseNames = sEmitCamelCaseNames = null;
tsDateHandler = sDateHandler = null;
tsThrowOnDeserializationError = sThrowOnDeserializationError = null;
HasSerializeFn = new HashSet<Type>();
TreatValueAsRefTypes = new HashSet<Type> {
typeof(KeyValuePair<,>)
};
}
#if MONOTOUCH
/// <summary>
/// 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.
/// </summary>
[MonoTouch.Foundation.Preserve]
public static void InitForAot() { }
[MonoTouch.Foundation.Preserve]
public static void RegisterForAot()
{
JsonAotConfig.Register<Poco>();
JsvAotConfig.Register<Poco>();
RegisterElement<Poco, string>();
RegisterElement<Poco, bool>();
// RegisterElement<Poco, char>();
// RegisterElement<Poco, byte>();
// RegisterElement<Poco, sbyte>();
// RegisterElement<Poco, short>();
// RegisterElement<Poco, ushort>();
RegisterElement<Poco, int>();
// RegisterElement<Poco, uint>();
RegisterElement<Poco, long>();
// RegisterElement<Poco, ulong>();
// RegisterElement<Poco, float>();
RegisterElement<Poco, double>();
RegisterElement<Poco, decimal>();
RegisterElement<Poco, Guid>();
RegisterElement<Poco, DateTime>();
// RegisterElement<Poco, TimeSpan>();
//
// RegisterElement<Poco, bool?>();
// RegisterElement<Poco, char?>();
// RegisterElement<Poco, byte?>();
// RegisterElement<Poco, sbyte?>();
// RegisterElement<Poco, short?>();
// RegisterElement<Poco, ushort?>();
// RegisterElement<Poco, int?>();
// RegisterElement<Poco, uint?>();
// RegisterElement<Poco, long?>();
// RegisterElement<Poco, ulong?>();
// RegisterElement<Poco, float?>();
// RegisterElement<Poco, double?>();
// RegisterElement<Poco, decimal?>();
// RegisterElement<Poco, Guid?>();
// RegisterElement<Poco, DateTime?>();
// RegisterElement<Poco, TimeSpan?>();
RegisterQueryStringWriter();
// RegisterCsvSerializer();
}
[MonoTouch.Foundation.Preserve]
static void RegisterQueryStringWriter()
{
var i = 0;
if (QueryStringWriter<Poco>.WriteFn() != null) i++;
}
// [MonoTouch.Foundation.Preserve]
// static void RegisterCsvSerializer()
// {
// CsvSerializer<Poco>.WriteFn();
// CsvSerializer<Poco>.WriteObject(null, null);
// CsvWriter<Poco>.WriteObject(null, null);
// CsvWriter<Poco>.WriteObjectRow(null, null);
// }
[MonoTouch.Foundation.Preserve]
public static void RegisterElement<T, TElement>()
{
JsonAotConfig.RegisterElement<T, TElement>();
JsvAotConfig.RegisterElement<T, TElement>();
}
#endif
}
#if MONOTOUCH
[MonoTouch.Foundation.Preserve(AllMembers=true)]
internal class Poco
{
public string Dummy { get; set; }
}
[MonoTouch.Foundation.Preserve(AllMembers=true)]
internal class JsonAotConfig
{
static JsReader<JsonTypeSerializer> reader;
static JsWriter<JsonTypeSerializer> writer;
static JsonTypeSerializer serializer;
static JsonAotConfig()
{
serializer = new JsonTypeSerializer();
reader = new JsReader<JsonTypeSerializer>();
writer = new JsWriter<JsonTypeSerializer>();
}
public static ParseStringDelegate GetParseFn(Type type)
{
var parseFn = JsonTypeSerializer.Instance.GetParseFn(type);
return parseFn;
}
internal static ParseStringDelegate RegisterBuiltin<T>()
{
var i = 0;
if (reader.GetParseFn<T>() != null) i++;
if (JsonReader<T>.GetParseFn() != null) i++;
if (JsonReader<T>.Parse(null) != null) i++;
if (JsonWriter<T>.WriteFn() != null) i++;
if (serializer.GetWriteFn<T>() != null) i++;
return serializer.GetParseFn<T>();
}
public static void Register<T>()
{
var i = 0;
var serializer = JsonTypeSerializer.Instance;
if (new List<T>() != null) i++;
if (new T[0] != null) i++;
if (serializer.GetParseFn<T>() != null) i++;
if (DeserializeArray<T[], JsonTypeSerializer>.Parse != null) i++;
JsConfig<T>.ExcludeTypeInfo = false;
//JsConfig<T>.SerializeFn = arg => "";
//JsConfig<T>.DeSerializeFn = arg => default(T);
DeserializeArrayWithElements<T, JsonTypeSerializer>.ParseGenericArray(null, null);
DeserializeCollection<JsonTypeSerializer>.ParseCollection<T>(null, null, null);
DeserializeListWithElements<T, JsonTypeSerializer>.ParseGenericList(null, null, null);
SpecializedQueueElements<T>.ConvertToQueue(null);
SpecializedQueueElements<T>.ConvertToStack(null);
WriteListsOfElements<T, JsonTypeSerializer>.WriteList(null, null);
WriteListsOfElements<T, JsonTypeSerializer>.WriteIList(null, null);
WriteListsOfElements<T, JsonTypeSerializer>.WriteEnumerable(null, null);
WriteListsOfElements<T, JsonTypeSerializer>.WriteListValueType(null, null);
WriteListsOfElements<T, JsonTypeSerializer>.WriteIListValueType(null, null);
JsonReader<T>.Parse(null);
JsonWriter<T>.WriteFn();
TranslateListWithElements<T>.LateBoundTranslateToGenericICollection(null, null);
TranslateListWithConvertibleElements<T, T>.LateBoundTranslateToGenericICollection(null, null);
QueryStringWriter<T>.WriteObject(null, null);
}
public static void RegisterElement<T, TElement>()
{
RegisterBuiltin<TElement>();
DeserializeDictionary<JsonTypeSerializer>.ParseDictionary<T, TElement>(null, null, null, null);
DeserializeDictionary<JsonTypeSerializer>.ParseDictionary<TElement, T>(null, null, null, null);
ToStringDictionaryMethods<T, TElement, JsonTypeSerializer>.WriteIDictionary(null, null, null, null);
ToStringDictionaryMethods<TElement, T, JsonTypeSerializer>.WriteIDictionary(null, null, null, null);
DeserializeArrayWithElements<TElement, JsonTypeSerializer>.ParseGenericArray(null, null);
// DeserializeCollection<JsonTypeSerializer>.ParseCollection<TElement>(null, null, null);
DeserializeListWithElements<TElement, JsonTypeSerializer>.ParseGenericList(null, null, null);
WriteListsOfElements<TElement, JsonTypeSerializer>.WriteList(null, null);
WriteListsOfElements<TElement, JsonTypeSerializer>.WriteIList(null, null);
WriteListsOfElements<TElement, JsonTypeSerializer>.WriteEnumerable(null, null);
WriteListsOfElements<TElement, JsonTypeSerializer>.WriteListValueType(null, null);
WriteListsOfElements<TElement, JsonTypeSerializer>.WriteIListValueType(null, null);
TranslateListWithElements<TElement>.LateBoundTranslateToGenericICollection(null, typeof(List<TElement>));
TranslateListWithConvertibleElements<TElement, TElement>.LateBoundTranslateToGenericICollection(null, typeof(List<TElement>));
}
}
[MonoTouch.Foundation.Preserve(AllMembers=true)]
internal class JsvAotConfig
{
static JsReader<JsvTypeSerializer> reader;
static JsWriter<JsvTypeSerializer> writer;
static JsvTypeSerializer serializer;
static JsvAotConfig()
{
serializer = new JsvTypeSerializer();
reader = new JsReader<JsvTypeSerializer>();
writer = new JsWriter<JsvTypeSerializer>();
}
public static ParseStringDelegate GetParseFn(Type type)
{
var parseFn = JsvTypeSerializer.Instance.GetParseFn(type);
return parseFn;
}
internal static ParseStringDelegate RegisterBuiltin<T>()
{
var i = 0;
if (reader.GetParseFn<T>() != null) i++;
if (JsvReader<T>.GetParseFn() != null) i++;
if (JsvReader<T>.Parse(null) != null) i++;
if (JsvWriter<T>.WriteFn() != null) i++;
if (serializer.GetWriteFn<T>() != null) i++;
return serializer.GetParseFn<T>();
}
public static void Register<T>()
{
var i = 0;
var serializer = JsvTypeSerializer.Instance;
if (new List<T>() != null) i++;
if (new T[0] != null) i++;
if (serializer.GetParseFn<T>() != null) i++;
if (DeserializeArray<T[], JsvTypeSerializer>.Parse != null) i++;
JsConfig<T>.ExcludeTypeInfo = false;
//JsConfig<T>.SerializeFn = arg => "";
//JsConfig<T>.DeSerializeFn = arg => default(T);
DeserializeArrayWithElements<T, JsvTypeSerializer>.ParseGenericArray(null, null);
DeserializeCollection<JsvTypeSerializer>.ParseCollection<T>(null, null, null);
DeserializeListWithElements<T, JsvTypeSerializer>.ParseGenericList(null, null, null);
SpecializedQueueElements<T>.ConvertToQueue(null);
SpecializedQueueElements<T>.ConvertToStack(null);
WriteListsOfElements<T, JsvTypeSerializer>.WriteList(null, null);
WriteListsOfElements<T, JsvTypeSerializer>.WriteIList(null, null);
WriteListsOfElements<T, JsvTypeSerializer>.WriteEnumerable(null, null);
WriteListsOfElements<T, JsvTypeSerializer>.WriteListValueType(null, null);
WriteListsOfElements<T, JsvTypeSerializer>.WriteIListValueType(null, null);
JsvReader<T>.Parse(null);
JsvWriter<T>.WriteFn();
TranslateListWithElements<T>.LateBoundTranslateToGenericICollection(null, null);
TranslateListWithConvertibleElements<T, T>.LateBoundTranslateToGenericICollection(null, null);
QueryStringWriter<T>.WriteObject(null, null);
}
public static void RegisterElement<T, TElement>()
{
RegisterBuiltin<TElement>();
DeserializeDictionary<JsvTypeSerializer>.ParseDictionary<T, TElement>(null, null, null, null);
DeserializeDictionary<JsvTypeSerializer>.ParseDictionary<TElement, T>(null, null, null, null);
ToStringDictionaryMethods<T, TElement, JsvTypeSerializer>.WriteIDictionary(null, null, null, null);
ToStringDictionaryMethods<TElement, T, JsvTypeSerializer>.WriteIDictionary(null, null, null, null);
DeserializeArrayWithElements<TElement, JsvTypeSerializer>.ParseGenericArray(null, null);
// DeserializeCollection<JsvTypeSerializer>.ParseCollection<TElement>(null, null, null);
DeserializeListWithElements<TElement, JsvTypeSerializer>.ParseGenericList(null, null, null);
WriteListsOfElements<TElement, JsvTypeSerializer>.WriteList(null, null);
WriteListsOfElements<TElement, JsvTypeSerializer>.WriteIList(null, null);
WriteListsOfElements<TElement, JsvTypeSerializer>.WriteEnumerable(null, null);
WriteListsOfElements<TElement, JsvTypeSerializer>.WriteListValueType(null, null);
WriteListsOfElements<TElement, JsvTypeSerializer>.WriteIListValueType(null, null);
TranslateListWithElements<TElement>.LateBoundTranslateToGenericICollection(null, typeof(List<TElement>));
TranslateListWithConvertibleElements<TElement, TElement>.LateBoundTranslateToGenericICollection(null, typeof(List<TElement>));
}
}
#endif
public class JsConfig<T> //where T : struct
{
/// <summary>
/// Never emit type info for this type
/// </summary>
public static bool ExcludeTypeInfo = false;
/// <summary>
/// <see langword="true"/> if the <see cref="ITypeSerializer"/> is configured
/// to take advantage of <see cref="CLSCompliantAttribute"/> specification,
/// to support user-friendly serialized formats, ie emitting camelCasing for JSON
/// and parsing member names and enum values in a case-insensitive manner.
/// </summary>
public static bool EmitCamelCaseNames = false;
/// <summary>
/// Define custom serialization fn for BCL Structs
/// </summary>
private static Func<T, string> serializeFn;
public static Func<T, string> SerializeFn
{
get { return serializeFn; }
set
{
serializeFn = value;
if (value != null)
JsConfig.HasSerializeFn.Add(typeof(T));
else
JsConfig.HasSerializeFn.Remove(typeof(T));
}
}
/// <summary>
/// Opt-in flag to set some Value Types to be treated as a Ref Type
/// </summary>
public bool TreatValueAsRefTypes
{
get { return JsConfig.TreatValueAsRefTypes.Contains(typeof (T)); }
set
{
if (value)
JsConfig.TreatValueAsRefTypes.Add(typeof(T));
else
JsConfig.TreatValueAsRefTypes.Remove(typeof(T));
}
}
/// <summary>
/// Define custom deserialization fn for BCL Structs
/// </summary>
public static Func<string, T> DeSerializeFn;
/// <summary>
/// Exclude specific properties of this type from being serialized
/// </summary>
public static string[] ExcludePropertyNames;
public static void WriteFn<TSerializer>(TextWriter writer, object obj)
{
var serializer = JsWriter.GetTypeSerializer<TSerializer>();
serializer.WriteString(writer, SerializeFn((T)obj));
}
public static object ParseFn(string str)
{
return DeSerializeFn(str);
}
}
public enum JsonDateHandler
{
TimestampOffset,
DCJSCompatible,
ISO8601
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment