diff --git a/ExternalCoreProjects/Virtu/ExtraConverters.cs b/ExternalCoreProjects/Virtu/ExtraConverters.cs index 743fe4bef9..389b6148d3 100644 --- a/ExternalCoreProjects/Virtu/ExtraConverters.cs +++ b/ExternalCoreProjects/Virtu/ExtraConverters.cs @@ -156,6 +156,85 @@ namespace Jellyfish.Virtu } } + public class TypeTypeConverter : JsonConverter + { + // serialize and deserialize types, ignoring assembly entirely and only using namespace+typename + // all types, including generic type arguments to supplied types, must be in one of the declared assemblies (only checked on read!) + // the main goal here is to have something with a slight chance of working across versions + + public TypeTypeConverter(IEnumerable ass) + { + assemblies = ass.ToList(); + } + + private List assemblies; + private Dictionary readlookup = new Dictionary(); + + public override bool CanConvert(Type objectType) + { + return typeof(Type).IsAssignableFrom(objectType); + } + + public override bool CanRead { get { return true; } } + public override bool CanWrite { get { return true; } } + + private Type GetType(string name) + { + Type ret; + if (!readlookup.TryGetValue(name, out ret)) + { + ret = assemblies.Select(ass => ass.GetType(name, false)).Where(t => t != null).Single(); + readlookup.Add(name, ret); + } + return ret; + } + + private static string GetName(Type type) + { + return string.Format("{0}.{1}", type.Namespace, type.Name); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + { + return null; + } + else if (reader.TokenType == JsonToken.String) + { + return GetType(reader.Value.ToString()); + } + else if (reader.TokenType == JsonToken.StartArray) // full generic + { + List vals = serializer.Deserialize>(reader); + return GetType(vals[0]).MakeGenericType(vals.Skip(1).Select(GetType).ToArray()); + } + else + { + throw new InvalidOperationException(); + } + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var type = (Type)value; + if (type.IsGenericType && !type.IsGenericTypeDefinition) + { + writer.WriteStartArray(); + writer.WriteValue(GetName(type)); + foreach (var t in type.GetGenericArguments()) + { + writer.WriteValue(GetName(t)); + } + writer.WriteEndArray(); + } + else + { + writer.WriteValue(GetName(type)); + } + } + } + public class DelegateConverter : JsonConverter { // caveats: if used on anonymous delegates and/or closures, brittle to name changes in the generated classes and methods diff --git a/ExternalCoreProjects/Virtu/Machine.cs b/ExternalCoreProjects/Virtu/Machine.cs index 958932c5af..47bf00bc15 100644 --- a/ExternalCoreProjects/Virtu/Machine.cs +++ b/ExternalCoreProjects/Virtu/Machine.cs @@ -123,6 +123,8 @@ namespace Jellyfish.Virtu private static JsonSerializer CreateSerializer() { + // TODO: converters could be cached for speedup + var ser = new JsonSerializer { TypeNameHandling = TypeNameHandling.Auto, @@ -130,8 +132,15 @@ namespace Jellyfish.Virtu ReferenceLoopHandling = ReferenceLoopHandling.Serialize, }; + ser.Converters.Add(new TypeTypeConverter(new[] + { + // all expected Types to convert are either in this assembly or mscorlib + typeof(Machine).Assembly, + typeof(object).Assembly + })); ser.Converters.Add(new DelegateConverter()); ser.Converters.Add(new ArrayConverter()); + var cr = new DefaultContractResolver(); cr.DefaultMembersSearchFlags |= System.Reflection.BindingFlags.NonPublic; ser.ContractResolver = cr; diff --git a/References/Virtu.dll b/References/Virtu.dll index 5d6306ceaa..8777c9db5e 100644 Binary files a/References/Virtu.dll and b/References/Virtu.dll differ