/// <summary>
        /// Returns info about the unleash setup.
        /// </summary>
        public override string ToString()
        {
            var sb = new StringBuilder("## Unleash settings ##");

            sb.AppendLine($"Application name: {AppName}");
            sb.AppendLine($"Environment: {Environment}");
            sb.AppendLine($"Instance tag: {InstanceTag}");
            sb.AppendLine($"Server Uri: {UnleashApi}");
            sb.AppendLine($"Sdk version: {SdkVersion}");

            sb.AppendLine($"Fetch toggles interval: {FetchTogglesInterval.TotalSeconds} seconds");
            var metricsInterval = SendMetricsInterval.HasValue
                ? $"{SendMetricsInterval.Value.TotalSeconds} seconds"
                : "never";

            sb.AppendLine($"Send metrics interval: {metricsInterval}");

            sb.AppendLine($"Local storage folder: {LocalStorageFolder()}");
            sb.AppendLine($"Backup file: {FeatureToggleFilename}");
            sb.AppendLine($"Etag file: {EtagFilename}");

            sb.AppendLine($"HttpClient Factory: {HttpClientFactory.GetType().Name}");
            sb.AppendLine($"Json serializer: {JsonSerializer.GetType().Name}");
            sb.AppendLine($"Context provider: {UnleashContextProvider.GetType().Name}");

            return(sb.ToString());
        }
        public void ShouldDecodeMessageSuccessfully()
        {
            var decoded = new JsonSerializer().Decode <SessionData>(_jsonEncoded);

            Assert.AreEqual(typeof(SessionData), decoded.GetType());
            Assert.AreEqual(decoded, _jsonStub);
        }
        public void TheNonGenericFactoryMethodReturnsTheCorrectTypeOfSerializer()
        {
            var genericSerializer = new JsonSerializer<JsonSerializerFactoryTests>();
            var interfaceSerializer = JsonSerializer.Create(typeof(JsonSerializerFactoryTests));

            Assert.That(interfaceSerializer.GetType(), Is.EqualTo(genericSerializer.GetType()));
        }
        public void TheNonGenericFactoryMethodReturnsTheCorrectTypeOfSerializer()
        {
            var genericSerializer   = new JsonSerializer <JsonSerializerFactoryTests>();
            var interfaceSerializer = JsonSerializer.Create(typeof(JsonSerializerFactoryTests));

            Assert.That(interfaceSerializer.GetType(), Is.EqualTo(genericSerializer.GetType()));
        }
Example #5
0
 /// <summary>
 /// Creates and initializes a new instance of the ManateeJsonSerializer class.
 /// </summary>
 public ManateeSerializer()
 {
     _serializer = new JsonSerializer
     {
         Options =
         {
             EnumSerializationFormat = EnumSerializationFormat.AsName,
             FlagsEnumSeparator      = ","
         }
     };
     _method = _serializer.GetType().GetMethod("Serialize");
 }
        private static object ReadJsonBase(JsonSerializer serializer, TypeInfo type, JsonReader reader)
        {
            if (reader.TokenType == JsonToken.None)
            {
                reader.Read();
            }
            var contract = serializer.ContractResolver.ResolveContract(type.AsType());
            var getInternalSerializer = serializer.GetType().GetTypeInfo().GetDeclaredMethod("GetInternalSerializer");
            var internalSerializer    = getInternalSerializer.Invoke(serializer, new object[0]);
            var createValue           = internalSerializer.GetType().GetTypeInfo().GetDeclaredMethod("CreateValueInternal");
            var result = createValue.Invoke(internalSerializer, new object[] { reader, type, contract, null, null, null, null });

            return(result);
        }
Example #7
0
 /// <summary>
 /// Creates and initializes a new instance of the ManateeJsonSerializer class.
 /// </summary>
 public ManateeSerializer()
 {
     _serializer = new JsonSerializer
     {
         Options =
         {
             EnumSerializationFormat = EnumSerializationFormat.AsName,
             FlagsEnumSeparator      = ","
         }
     };
     _serializer.CustomSerializations.RegisterType(DateTimeToJson, JsonToDateTime);
     InitializeAbstractionMap(_serializer);
     _method = _serializer.GetType().GetTypeInfo().DeclaredMethods
               .First(m => m.Name == "Serialize")
               .GetGenericMethodDefinition();
 }
Example #8
0
        public T Deserialize <T>(IRestResponse response)
        {
            var result = Activator.CreateInstance <T>();
            var events = result as List <Event>;

            if (events == null)
            {
                throw new InvalidOperationException("The type must be List<Event>.");
            }

            var json = JArray.Parse(response.Content);

            serializer = new JsonSerializer();

            var deserializeMethod = serializer.GetType().GetMethods().First(m => m.IsGenericMethod && m.Name == "Deserialize");
            var ns               = typeof(Event).Namespace;
            var userType         = typeof(User);
            var repositoryType   = typeof(Repository);
            var userDeserializer = deserializeMethod.MakeGenericMethod(userType);
            var repoDeserializer = deserializeMethod.MakeGenericMethod(repositoryType);

            foreach (var eventData in json)
            {
                try
                {
                    var payload = eventData.SelectToken("payload");

                    var eventDeserializer = deserializeMethod.MakeGenericMethod(Type.GetType(string.Format("{0}.{1}", ns, eventData["type"].Value <String>())));
                    var e = (Event)eventDeserializer.Invoke(serializer, new object[] { new JTokenReader(payload) });

                    e.Actor     = (User)userDeserializer.Invoke(serializer, new object[] { new JTokenReader(eventData["actor"]) });
                    e.Public    = eventData["public"].Value <bool?>();
                    e.CreatedAt = eventData["created_at"].Value <DateTime>();
                    e.Repo      = (Repository)repoDeserializer.Invoke(serializer, new object[] { new JTokenReader(eventData["repo"]) });

                    events.Add(e);
                }
                catch (Exception)
                {
                    // might silencly fail on an event
                }
            }

            return(result);
        }
Example #9
0
        public static void ApplyCustomSettings(this JsonSerializer serializer,
                                               JsonSerializerSettings settings)
        {
            if (settings == null)
            {
                return;
            }

            foreach (PropertyInfo prop in serializer.GetType().GetProperties())
            {
                try
                {
                    var value = settings.GetType().GetProperty(prop.Name).GetValue(settings);
                    if (value == null)
                    {
                        continue;
                    }
                    prop.SetValue(serializer, value);
                }
                catch { }
            }
        }
Example #10
0
            public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
            {
                if (reader.TokenType == JsonToken.Null)
                {
                    return(null);
                }
                if (objectType.IsAbstract)
                {
                    JObject jo = JObject.Load(reader);
                    Type    boType;
                    if (serializer.TypeNameHandling.HasFlag(TypeNameHandling.Objects) && jo.TryGetValue("$type", out JToken typeToken))
                    {
                        boType = BusinessObjectSerializationBinder.BusinessObjectAndCOMTypes.SingleOrDefault(t => typeToken.Value <string>().ToUpper().StartsWith(t.FullName.ToUpper()));
                    }
                    else if (!jo.ContainsKey("boTyp"))
                    {
                        throw new ArgumentException("If deserializing into an abstract BusinessObject the key \"boTyp\" has to be set. But it wasn't.");
                    }
                    else
                    {
#pragma warning disable CS0618                                                            // Type or member is obsolete
                        boType = BoMapper.GetTypeForBoName(jo["boTyp"].Value <string>()); // ToDo: catch exception if boTyp is not set and throw exception with descriptive error message
#pragma warning restore CS0618                                                            // Type or member is obsolete
                    }
                    if (boType == null)
                    {
                        foreach (var assembley in AppDomain.CurrentDomain.GetAssemblies())
                        {
                            try
                            {
                                boType = assembley.GetTypes().FirstOrDefault(x => x.Name.ToUpper() == jo["boTyp"].Value <string>().ToUpper());
                            }
                            catch (ReflectionTypeLoadException)
                            {
                                continue;
                            }
                            if (boType != null)
                            {
                                break;
                            }
                        }
                        if (boType == null)
                        {
                            throw new NotImplementedException($"The type '{jo["boTyp"].Value<string>()}' does not exist in the BO4E standard.");
                        }
                    }
                    var deserializationMethod = serializer.GetType() // https://stackoverflow.com/a/5218492/10009545
                                                .GetMethods()
                                                .Where(m => m.Name == nameof(serializer.Deserialize))
                                                .Select(m => new
                    {
                        Method = m,
                        Params = m.GetParameters(),
                        Args   = m.GetGenericArguments()
                    })
                                                .Where(x => x.Params.Length == 1 &&
                                                       x.Args.Length == 1)
                                                .Select(x => x.Method)
                                                .First()
                                                .GetGenericMethodDefinition()
                                                .MakeGenericMethod(new Type[] { boType });
                    try
                    {
                        return(deserializationMethod.Invoke(serializer, new object[] { jo.CreateReader() }));
                    }
                    catch (TargetInvocationException tie) when(tie.InnerException != null)
                    {
                        throw tie.InnerException; // to hide the reflection to the outside.
                    }
                }
                else
                {
                    serializer.ContractResolver.ResolveContract(objectType).Converter = null;
                    return(serializer.Deserialize(JObject.Load(reader).CreateReader(), objectType));
                }
            }
Example #11
0
        static JsonUtility()
        {
            serializer = new JsonSerializer
            {
                DateParseHandling = DateParseHandling.None,
                Converters        =
                {
                    new UtcDateTimeConverter()
                }
            };
            serializableTypes = new HashSet <Type>();

            // Register converters for types implementing IJsonSerializable or that have DataContract attributes
            // Include all types in ExoWeb and ExoRule automatically
            foreach (var converter in JsonConverter.Infer(
                         typeof(ServiceHandler).Assembly.GetTypes().Union(
                             typeof(Rule).Assembly.GetTypes().Where(type => typeof(Rule).IsAssignableFrom(type)))))
            {
                RegisterConverter(converter);
            }

            // Deserialize Value Change Event
            Func <JsonReader, ModelValueChangeEvent> deserializeValueChangeEvent = (reader) =>
            {
                string             p;
                ModelInstance      instance = null;
                ModelValueProperty property = null;
                object             oldValue = null;
                object             newValue = null;
                while (reader.ReadProperty(out p))
                {
                    switch (p)
                    {
                    case "instance":
                        instance = reader.ReadValue <ModelInstance>();
                        break;

                    case "property":
                        var propertyName = reader.ReadValue <string>();
                        property = (ModelValueProperty)instance.Type.Properties[propertyName];
                        if (property == null)
                        {
                            throw new SerializationException("Unable to deserialize ValueChange: property \"" + propertyName + "\" does not exist for type \"" + instance.Type.Name + "\".");
                        }
                        break;

                    case "oldValue":
                        if (reader.TokenType == JsonToken.StartObject)
                        {
                            oldValue = reader.ReadValue(property.PropertyType);
                        }
                        else
                        {
                            oldValue = property.CoerceValue(reader.Value);
                            reader.Read();
                        }
                        break;

                    case "newValue":
                        if (reader.TokenType == JsonToken.StartObject)
                        {
                            newValue = reader.ReadValue(property.PropertyType);
                        }
                        else
                        {
                            newValue = property.CoerceValue(reader.Value);
                            reader.Read();
                        }
                        break;

                    default:
                        throw new ArgumentException("The specified property could not be deserialized.", p);
                    }
                }
                return(new ModelValueChangeEvent(instance, property, oldValue, newValue));
            };

            // Deserialize Reference Change Event
            Func <JsonReader, ModelReferenceChangeEvent> deserializeReferenceChangeEvent = (reader) =>
            {
                string                 p;
                ModelInstance          instance = null;
                ModelReferenceProperty property = null;
                ModelInstance          oldValue = null;
                ModelInstance          newValue = null;
                while (reader.ReadProperty(out p))
                {
                    switch (p)
                    {
                    case "instance":
                        instance = reader.ReadValue <ModelInstance>();
                        break;

                    case "property":
                        var propertyName = reader.ReadValue <string>();
                        property = (ModelReferenceProperty)instance.Type.Properties[propertyName];
                        if (property == null)
                        {
                            throw new SerializationException("Unable to deserialize ReferenceChange: property \"" + propertyName + "\" does not exist for type \"" + instance.Type.Name + "\".");
                        }
                        break;

                    case "oldValue":
                        oldValue = reader.ReadValue <ModelInstance>();
                        break;

                    case "newValue":
                        newValue = reader.ReadValue <ModelInstance>();
                        break;

                    default:
                        throw new ArgumentException(@"The specified property could not be deserialized.", p);
                    }
                }
                return(new ModelReferenceChangeEvent(instance, property, oldValue, newValue));
            };

            // Deserialize List Change Event
            Func <JsonReader, ModelListChangeEvent> deserializeListChangeEvent = (reader) =>
            {
                string                 p;
                ModelInstance          instance = null;
                ModelReferenceProperty property = null;
                ModelInstance[]        added    = null;
                ModelInstance[]        removed  = null;
                while (reader.ReadProperty(out p))
                {
                    switch (p)
                    {
                    case "instance":
                        instance = reader.ReadValue <ModelInstance>();
                        break;

                    case "property":
                        var propertyName = reader.ReadValue <string>();
                        property = (ModelReferenceProperty)instance.Type.Properties[propertyName];
                        if (property == null)
                        {
                            throw new SerializationException("Unable to deserialize ListChange: property \"" + propertyName + "\" does not exist for type \"" + instance.Type.Name + "\".");
                        }
                        break;

                    case "added":
                        added = reader.ReadValue <ModelInstance[]>();
                        break;

                    case "removed":
                        removed = reader.ReadValue <ModelInstance[]>();
                        break;

                    default:
                        throw new ArgumentException(@"The specified property could not be deserialized.", p);
                    }
                }
                return(new ModelListChangeEvent(instance, property, added, removed));
            };

            // Deserialize Init New Event
            Func <JsonReader, ModelInitEvent.InitNew> deserializeInitNewEvent = (reader) =>
            {
                string        p;
                ModelInstance instance = null;
                while (reader.ReadProperty(out p))
                {
                    switch (p)
                    {
                    case "instance":
                        instance = reader.ReadValue <ModelInstance>();
                        break;

                    default:
                        throw new ArgumentException(@"The specified property could not be deserialized.", p);
                    }
                }
                return(new ModelInitEvent.InitNew(instance));
            };

            // Deserialize Init Existing Event
            Func <JsonReader, ModelInitEvent.InitExisting> deserializeInitExistingEvent = (reader) =>
            {
                string        p;
                ModelInstance instance = null;
                while (reader.ReadProperty(out p))
                {
                    switch (p)
                    {
                    case "instance":
                        instance = reader.ReadValue <ModelInstance>();
                        break;

                    default:
                        throw new ArgumentException(@"The specified property could not be deserialized.", p);
                    }
                }
                return(new ModelInitEvent.InitExisting(instance));
            };

            // Deserialize Delete Event
            Func <JsonReader, ModelDeleteEvent> deserializeDeleteEvent = (reader) =>
            {
                string        p;
                ModelInstance instance        = null;
                bool          isPendingDelete = false;
                while (reader.ReadProperty(out p))
                {
                    switch (p)
                    {
                    case "instance":
                        instance = reader.ReadValue <ModelInstance>();
                        break;

                    case "isPendingDelete":
                        isPendingDelete = reader.ReadValue <bool>();
                        break;

                    default:
                        throw new ArgumentException(@"The specified property could not be deserialized.", p);
                    }
                }
                return(new ModelDeleteEvent(instance, isPendingDelete));
            };

            // Construct Model Instance
            var createModelInstance = typeof(ModelInstance).GetConstructor(
                BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
                null,
                new Type[] { typeof(ModelType), typeof(string) },
                null);

            // Register custom converters for ModelType, ModelProperty, ModelMethod, ModelInstance, ModelEvent
            foreach (var converter in
                     new JsonConverter[]
            {
                // Model Type
                new JsonConverter <ModelType>(
                    (modelType, json) =>
                {
                    // Base Type
                    if (modelType.BaseType != null)
                    {
                        json.Set("baseType", JsonConverter.GetJsonReferenceType(modelType.BaseType));
                    }

                    // Base Type
                    if (!String.IsNullOrEmpty(modelType.Format))
                    {
                        json.Set("format", modelType.Format);
                    }

                    // Properties
                    if (modelType.Properties.Any())
                    {
                        json.Set("properties", modelType.Properties
                                 .Where(property => property.DeclaringType == modelType && ExoWeb.IncludeInClientModel(property))
                                 .ToDictionary(property => property.Name));
                    }

                    // Methods
                    if (modelType.Methods.Any())
                    {
                        json.Set("methods", modelType.Methods.ToDictionary(method => method.Name));
                    }

                    // Rules
                    var rules = Rule.GetRegisteredRules(modelType).ToList();
                    var typeRules = rules.Where(rule => (rule.ExecutionLocation & RuleExecutionLocation.Client) > 0 && !(rule is IPropertyRule)).ToArray();
                    if (typeRules.Any())
                    {
                        json.Set("rules", typeRules);
                    }

                    // Condition Types
                    var serverConditionTypes = rules
                                               .Where(rule => (rule.ExecutionLocation & RuleExecutionLocation.Client) == 0)
                                               .SelectMany(rule => rule.ConditionTypes)
                                               .ToArray();
                    if (serverConditionTypes.Any())
                    {
                        json.Set("conditionTypes", serverConditionTypes);
                    }

                    // Exports
                    var exports = json.Global <Dictionary <string, string> >("exports");
                    if (exports.Any())
                    {
                        json.Set("exports", json.Global <Dictionary <string, string> >("exports"));
                    }
                },
                    json => { throw new NotSupportedException("ModelType cannot be deserialized."); }),

                // Model Property
                new JsonConverter <ModelProperty>(
                    (property, json) =>
                {
                    // Type
                    string type = (property is ModelValueProperty ?
                                   JsonConverter.GetJsonValueType(((ModelValueProperty)property).PropertyType) ?? "Object" :
                                   JsonConverter.GetJsonReferenceType(((ModelReferenceProperty)property).PropertyType)) +
                                  (property.IsList ? "[]" : "");
                    json.Set("type", type);

                    // IsStatic
                    if (property.IsStatic)
                    {
                        json.Set("isStatic", true);
                    }

                    // IsPersisted
                    if (!property.IsPersisted && !property.IsStatic)
                    {
                        json.Set("isPersisted", false);
                    }

                    // IsCalculated
                    if (property.IsCalculated)
                    {
                        json.Set("isCalculated", true);
                    }

                    // Index
                    int index = 0;
                    foreach (ModelProperty p in property.DeclaringType.Properties)
                    {
                        if (p == property)
                        {
                            break;
                        }
                        if (ExoWeb.IncludeInClientModel(p) && !p.IsStatic)
                        {
                            index++;
                        }
                    }
                    if (!property.IsStatic)
                    {
                        json.Set("index", index);
                    }

                    // Format
                    string format = property.Format;
                    if (!string.IsNullOrEmpty(format))
                    {
                        json.Set("format", format);
                    }

                    // Label
                    string label = property.Label;
                    if (!string.IsNullOrEmpty(label))
                    {
                        json.Set("label", label);
                    }

                    // Help Text
                    string helptext = property.HelpText;
                    if (!string.IsNullOrEmpty(helptext))
                    {
                        json.Set("helptext", helptext);
                    }

                    // Rules
                    var rules = Rule
                                .GetRegisteredRules(property.DeclaringType)
                                .Where(rule => (rule.ExecutionLocation & RuleExecutionLocation.Client) > 0 && rule is IPropertyRule && rule.RootType.Properties[((IPropertyRule)rule).Property] == property)
                                .ToDictionary(rule =>
                                              rule is ICalculationRule ? "calculated" :
                                              String.Format("{0}.{1}.{2}", rule.RootType.Name, ((IPropertyRule)rule).Property, ((IPropertyRule)rule).Name) == rule.Name ?
                                              ((IPropertyRule)rule).Name.Substring(0, 1).ToLower() + ((IPropertyRule)rule).Name.Substring(1) :
                                              rule.Name);
                    if (rules.Any())
                    {
                        json.Set("rules", rules);
                    }

                    // Default Value
                    if (property is ModelValueProperty)
                    {
                        var defaultValue = ((ModelValueProperty)property).DefaultValue;
                        if (defaultValue != null)
                        {
                            json.Set("defaultValue", ((ModelValueProperty)property).DefaultValue);
                        }
                    }
                },
                    json => { throw new NotSupportedException("ModelProperty cannot be deserialized."); }),

                // Model Method
                new JsonConverter <ModelMethod>(
                    (method, json) =>
                {
                    // Parameters
                    json.Set("parameters", method.Parameters.Select(p => p.Name));

                    // IsStatic
                    json.Set("isStatic", method.IsStatic);
                },
                    json => { throw new NotSupportedException("ModelMethod cannot be deserialized."); }),

                // Model Instance
                new JsonConverter <ModelInstance>(
                    (instance, json) =>
                {
                    json.Set("id", instance.Id);
                    json.Set("type", instance.Type.Name);
                },
                    reader =>
                {
                    string p;
                    ModelType type = null;
                    string id = null;
                    while (reader.ReadProperty(out p))
                    {
                        switch (p)
                        {
                        case "type":
                            type = ModelContext.Current.GetModelType(reader.ReadValue <string>());
                            break;

                        case "id":
                            id = reader.ReadValue <string>();
                            break;

                        // Ignore
                        case "isNew":
                            reader.ReadValue <bool>();
                            break;

                        default:
                            throw new ArgumentException(@"The specified property could not be deserialized.", p);
                        }
                    }
                    return((ModelInstance)createModelInstance.Invoke(new object[] { type, id }));
                }),

                // Model Event
                new JsonConverter <ModelEvent>(
                    (modelEvent, json) => { throw new NotSupportedException("ModelEvent cannot be deserialized."); },
                    (reader) =>
                {
                    string p;
                    if (reader.ReadProperty(out p) && p == "type")
                    {
                        string eventName = reader.ReadValue <string>();
                        switch (eventName)
                        {
                        case "ValueChange": return(deserializeValueChangeEvent(reader));

                        case "ReferenceChange": return(deserializeReferenceChangeEvent(reader));

                        case "ListChange": return(deserializeListChangeEvent(reader));

                        case "InitNew": return(deserializeInitNewEvent(reader));

                        case "InitExisting": return(deserializeInitExistingEvent(reader));

                        case "Delete": return(deserializeDeleteEvent(reader));
                        }
                        throw new NotSupportedException(eventName + " event cannot be deserialized.");
                    }
                    else
                    {
                        throw new FormatException("The type parameter 'type' must be the first serialized value in model event json.");
                    }
                }),

                // Model Value Change Event
                new JsonConverter <ModelValueChangeEvent>(
                    (modelEvent, json) =>
                {
                    //Property sequence matters
                    json.Set("type", "ValueChange");
                    json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId));
                    json.Set("property", modelEvent.Property.Name);
                    json.Set("oldValue", modelEvent.OldValue);
                    json.Set("newValue", modelEvent.NewValue);
                },
                    deserializeValueChangeEvent),

                // Model Reference Change Event
                new JsonConverter <ModelReferenceChangeEvent>(
                    (modelEvent, json) =>
                {
                    json.Set("type", "ReferenceChange");
                    json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId));
                    json.Set("property", modelEvent.Property.Name);
                    json.Set("oldValue", GetEventInstance(modelEvent.OldValue, modelEvent.OldValueId));
                    json.Set("newValue", GetEventInstance(modelEvent.NewValue, modelEvent.NewValueId));
                },
                    deserializeReferenceChangeEvent),

                // Model List Change Event
                new JsonConverter <ModelListChangeEvent>(
                    (modelEvent, json) =>
                {
                    json.Set("type", "ListChange");
                    json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId));
                    json.Set("property", modelEvent.Property.Name);
                    json.Set("added", modelEvent.Added.Select((instance, index) => GetEventInstance(instance, modelEvent.AddedIds.ElementAt(index))));
                    json.Set("removed", modelEvent.Removed.Select((instance, index) => GetEventInstance(instance, modelEvent.RemovedIds.ElementAt(index))));
                },
                    deserializeListChangeEvent),

                // Model Init New Event
                new JsonConverter <ModelInitEvent.InitNew>(
                    (modelEvent, json) =>
                {
                    json.Set("type", "InitNew");
                    json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId));
                },
                    deserializeInitNewEvent),

                // Model Init Existing Event
                new JsonConverter <ModelInitEvent.InitExisting>(
                    (modelEvent, json) =>
                {
                    json.Set("type", "InitExisting");
                    json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId));
                },
                    deserializeInitExistingEvent),

                // Model Delete Event
                new JsonConverter <ModelDeleteEvent>(
                    (modelEvent, json) =>
                {
                    json.Set("type", "Delete");
                    json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId));
                    json.Set("isPendingDelete", modelEvent.IsPendingDelete);
                },
                    deserializeDeleteEvent),

                // Model Save Event
                new JsonConverter <ModelSaveEvent>(
                    (modelEvent, json) =>
                {
                    json.Set("type", "Save");
                    json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId));
                    json.Set("added", modelEvent.Added.Select(instance => new Dictionary <string, string>()
                    {
                        { "type", instance.Type.Name }, { "oldId", instance.OriginalId }, { "newId", instance.Id }
                    }));
                    json.Set("modified", modelEvent.Modified);
                    json.Set("deleted", modelEvent.Deleted);
                },
                    json => { throw new NotSupportedException("ModelSaveEvent cannot be deserialized."); }),

                // Condition Type
                new JsonConverter <ConditionType>(
                    (conditionType, json) =>
                {
                    json.Set("code", conditionType.Code);
                    json.Set("category", conditionType.Category.ToString());

                    if (conditionType.Sets != null && conditionType.Sets.Any())
                    {
                        json.Set("sets", conditionType.Sets.Select(set => set.Name));
                    }

                    json.Set("message", conditionType.Message);
                },
                    json => { throw new NotSupportedException("ConditionType cannot be deserialized."); }),

                // Condition
                new JsonConverter <Condition>(
                    (condition, json) =>
                {
                    if (condition.Message != condition.Type.Message)
                    {
                        json.Set("message", condition.Message);
                    }
                    json.Set("targets", condition.Targets.Where(ct => ct.Target != null));
                },
                    json => { throw new NotSupportedException("Condition cannot be deserialized."); }),

                // Condition Target
                new JsonConverter <ConditionTarget>(
                    (conditionTarget, json) =>
                {
                    json.Set("instance", conditionTarget.Target);
                    json.Set("properties", conditionTarget.Properties);
                },
                    json => { throw new NotSupportedException("ConditionTarget cannot be deserialized."); }),

                // Rule
                new JsonConverter <Rule>(
                    (rule, json) =>
                {
                    if (rule is ICalculationRule)
                    {
                        var calculation = (ICalculationRule)rule;
                        json.Set("onChangeOf", calculation.Predicates);
                        if (rule is IClientCalculationRule)
                        {
                            var clientCalc = (IClientCalculationRule)rule;
                            json.Set("calculate", clientCalc.FunctionBody);
                            foreach (var export in clientCalc.Exports)
                            {
                                json.Global <Dictionary <string, string> >("exports")[export.Key] = export.Value;
                            }
                        }
                        else
                        {
                            json.Set("calculate", calculation.Calculation);
                        }
                    }

                    else if (rule is IConditionRule)
                    {
                        var condition = (IConditionRule)rule;
                        json.Set("type", "condition");
                        json.Set("properties", condition.Properties);
                        json.Set("onChangeOf", condition.Predicates);
                        json.Set("conditionType", condition.ConditionType);
                        json.Set("assert", condition.Condition);
                    }

                    else
                    {
                        throw new NotSupportedException("Rules of type " + rule.GetType().FullName + " cannot be serialized.  Call ExoWeb.RegisterConverters() to register a converter to support serializing rules of this type.");
                    }
                },
                    json => { throw new NotSupportedException("Rule cannot be deserialized."); }),

                // AllowedValuesRule
                new JsonConverter <AllowedValuesRule>(
                    (rule, json) =>
                {
                    SerializePropertyRule(rule, json);
                    var sourceExpression = rule.SourceExpression;
                    if (sourceExpression != null)
                    {
                        json.Set("fn", sourceExpression.Expression);
                        if (!String.IsNullOrEmpty(rule.Path))
                        {
                            json.Set("onChangeOf", new string[] { rule.Path });
                        }
                    }
                    else
                    {
                        json.Set("source", rule.Source);
                    }
                    if (rule.IgnoreValidation)
                    {
                        json.Set("ignoreValidation", rule.IgnoreValidation);
                    }
                },
                    json => { throw new NotSupportedException("AllowedValuesRule cannot be deserialized."); }),

                // CompareRule
                new JsonConverter <CompareRule>(
                    (rule, json) =>
                {
                    SerializePropertyRule(rule, json);
                    json.Set("compareOperator", rule.CompareOperator.ToString());
                    json.Set("compareSource", rule.CompareSource);
                },
                    json => { throw new NotSupportedException("CompareRule cannot be deserialized."); }),

                // ListLengthRule
                new JsonConverter <ListLengthRule>(
                    (rule, json) =>
                {
                    SerializePropertyRule(rule, json);

                    // Min
                    if (rule.MinExpression != null)
                    {
                        json.Set("minFn", rule.MinExpression.Expression);
                    }
                    else
                    {
                        json.Set("min", rule.Minimum);
                    }

                    // Max
                    if (rule.MaxExpression != null)
                    {
                        json.Set("maxFn", rule.MaxExpression.Expression);
                    }
                    else
                    {
                        json.Set("max", rule.Maximum);
                    }

                    // OnChangeOf
                    if (!String.IsNullOrEmpty(rule.Path))
                    {
                        json.Set("onChangeOf", new string[] { rule.Path });
                    }
                },
                    json => { throw new NotSupportedException("ListLengthRule cannot be deserialized."); }),

                // RangeRule
                new JsonConverter <RangeRule>(
                    (rule, json) =>
                {
                    SerializePropertyRule(rule, json);

                    // Min
                    if (rule.MinExpression != null)
                    {
                        json.Set("minFn", rule.MinExpression.Expression);
                    }
                    else
                    {
                        json.Set("min", rule.Minimum);
                    }

                    // Max
                    if (rule.MaxExpression != null)
                    {
                        json.Set("maxFn", rule.MaxExpression.Expression);
                    }
                    else
                    {
                        json.Set("max", rule.Maximum);
                    }

                    // OnChangeOf
                    if (!String.IsNullOrEmpty(rule.Path))
                    {
                        json.Set("onChangeOf", new string[] { rule.Path });
                    }
                },
                    json => { throw new NotSupportedException("RangeRule cannot be deserialized."); }),

                // ValidationRule
                new JsonConverter <ValidationRule>(
                    (rule, json) =>
                {
                    SerializePropertyRule(rule, json);

                    // Validation
                    json.Set("isError", rule.ValidationExpression.Expression);

                    // ErrorMessage
                    if (rule.ErrorMessageResource != null)
                    {
                        json.Set("message", rule.ErrorMessageResource);
                    }
                    else
                    {
                        json.Set("message", rule.ErrorMessageExpression.Expression);
                    }

                    json.Set("properties", rule.AdditionalTargets);

                    // OnChangeOf
                    if (!String.IsNullOrEmpty(rule.Path))
                    {
                        json.Set("onChangeOf", new string[] { rule.Path });
                    }
                },
                    json => { throw new NotSupportedException("ValidationRule cannot be deserialized."); }),

                // RequiredRule
                new JsonConverter <RequiredRule>(
                    (rule, json) =>
                {
                    SerializePropertyRule(rule, json);
                    if (rule.RequiredValue != null)
                    {
                        json.Set("requiredValue", rule.RequiredValue);
                    }
                },
                    json => { throw new NotSupportedException("RequiredRule cannot be deserialized."); }),

                // RequiredIfRule
                new JsonConverter <RequiredIfRule>(
                    (rule, json) =>
                {
                    SerializePropertyRule(rule, json);
                    if (rule.RequiredExpression != null)
                    {
                        json.Set("fn", rule.RequiredExpression.Expression);

                        // OnChangeOf
                        if (!String.IsNullOrEmpty(rule.Path))
                        {
                            json.Set("onChangeOf", new string[] { rule.Path });
                        }
                    }
                    else
                    {
                        json.Set("compareOperator", rule.CompareOperator.ToString());
                        json.Set("compareSource", rule.CompareSource);
                        json.Set("compareValue", rule.CompareValue);
                    }

                    if (rule.RequiredValue != null)
                    {
                        json.Set("requiredValue", rule.RequiredValue);
                    }
                },
                    json => { throw new NotSupportedException("RequiredIfRule cannot be deserialized."); }),

                // OwnerRule
                new JsonConverter <OwnerRule>(
                    (rule, json) => SerializePropertyRule(rule, json),
                    json => { throw new NotSupportedException("OwnerRule cannot be deserialized."); }),

                // StringLengthRule
                new JsonConverter <StringLengthRule>(
                    (rule, json) =>
                {
                    SerializePropertyRule(rule, json);
                    if (rule.Minimum > 0)
                    {
                        json.Set("min", rule.Minimum);
                    }
                    if (rule.Maximum > 0)
                    {
                        json.Set("max", rule.Maximum);
                    }
                },
                    json => { throw new NotSupportedException("StringLengthRule cannot be deserialized."); }),

                // StringFormatRule
                new JsonConverter <StringFormatRule>(
                    (rule, json) =>
                {
                    SerializePropertyRule(rule, json);
                    if (!String.IsNullOrEmpty(rule.FormatDescription))
                    {
                        json.Set("description", rule.FormatDescription);
                    }
                    json.Set("expression", rule.FormatExpression.ToString());
                    if (!String.IsNullOrEmpty(rule.ReformatExpression))
                    {
                        json.Set("reformat", rule.ReformatExpression);
                    }
                },
                    json => { throw new NotSupportedException("StringFormatRule cannot be deserialized."); }),
            })
            {
                JsonUtility.RegisterConverter(converter);
            }

            // Cache the method info of the deserialize method
            // The non-generic version of this method was added in .NET 4.0
            deserialize = serializer.GetType().GetMethod("Deserialize", new Type[] { typeof(string) });
        }