/// <summary> /// Read the specified element /// </summary> public object ReadElementUtil(JsonReader r, Type t, JsonSerializationContext context) { var nonGenericT = t.StripGeneric(); var classifier = this.GetClassifier(nonGenericT); switch (r.TokenType) { case JsonToken.StartObject: { var formatter = this.GetFormatter(nonGenericT); bool isList = typeof(IList).GetTypeInfo().IsAssignableFrom(t.GetTypeInfo()); // Classifier??? if (classifier == null || !isList) { if (isList) { var retVal = Activator.CreateInstance(t) as IList; retVal.Add(formatter.Deserialize(r, t, context)); return(retVal); } else { return(formatter.Deserialize(r, t, context)); } } else { // Classifier each of these properties aren't real properties, rather they are classified things int depth = r.Depth; Dictionary <String, Object> values = new Dictionary <string, object>(); while (r.Read() && !(r.TokenType == JsonToken.EndObject && r.Depth == depth)) { // Classifier if (r.TokenType != JsonToken.PropertyName) { throw new JsonException($"Expected PropertyName token got {r.TokenType}"); } string propertyName = (String)r.Value; r.Read(); // Read proeprty name values.Add(propertyName, this.ReadElementUtil(r, r.TokenType == JsonToken.StartObject ? nonGenericT : t, new JsonSerializationContext(propertyName, this, values, context))); } return(classifier.Compose(values, t)); } } // Array read, we want to re-call the specified parse case JsonToken.StartArray: { if (!typeof(IList).GetTypeInfo().IsAssignableFrom(t.GetTypeInfo())) { throw new JsonSerializationException($"{t} does not implement IList at {r.Path}"); } int depth = r.Depth; var listInstance = Activator.CreateInstance(t) as IList; while (r.Read() && !(r.TokenType == JsonToken.EndArray && r.Depth == depth)) { listInstance.Add(this.ReadElementUtil(r, nonGenericT, context)); } return(listInstance); } case JsonToken.Null: case JsonToken.Boolean: case JsonToken.Bytes: case JsonToken.Float: case JsonToken.Date: case JsonToken.Integer: case JsonToken.String: if (typeof(IdentifiedData).GetTypeInfo().IsAssignableFrom(nonGenericT.GetTypeInfo())) // Complex object { var formatter = this.GetFormatter(nonGenericT); return(formatter.FromSimpleValue(r.Value)); } else { // Not a simple value attribute so let's just handle it as normal switch (r.TokenType) { case JsonToken.Null: return(null); case JsonToken.Boolean: case JsonToken.Bytes: return(r.Value); case JsonToken.Float: if (t.StripNullable() == typeof(Decimal)) { return(Convert.ToDecimal(r.Value)); } else if (t.StripNullable() == typeof(Int32)) { return(Convert.ToInt32(r.Value)); } else { return((Double)r.Value); } case JsonToken.Date: t = t.StripNullable(); if (t == typeof(DateTime)) { return((DateTime)r.Value); } else if (t == typeof(String)) { return(((DateTime)r.Value).ToString("o")); } else { return(new DateTimeOffset((DateTime)r.Value)); } case JsonToken.Integer: t = t.StripNullable(); if (t.GetTypeInfo().IsEnum) { return(Enum.ToObject(t, r.Value)); } return(Convert.ChangeType(r.Value, t)); case JsonToken.String: if (String.IsNullOrEmpty((string)r.Value)) { return(null); } else if (t.StripNullable() == typeof(Guid)) { return(Guid.Parse((string)r.Value)); } else if (t.StripNullable() == typeof(Int32)) { return("NaN".Equals(r.Value) ? 0 : Int32.Parse((String)r.Value)); } else if (t.StripNullable() == typeof(Decimal)) { return(Decimal.Parse((String)r.Value)); } else if (t.StripNullable() == typeof(byte[])) { return(Convert.FromBase64String((String)r.Value)); } else { return(r.Value); } default: return(r.Value); } } default: throw new JsonSerializationException("Invalid serialization"); } }
/// <summary> /// De-serialize the specified object from the class /// </summary> public Object Deserialize(JsonReader r, Type asType, JsonSerializationContext context) { var retVal = new Object(); if (!asType.GetTypeInfo().IsAbstract) { retVal = Activator.CreateInstance(asType); } int depth = r.TokenType == JsonToken.StartObject ? r.Depth : r.Depth - 1; // current depth var properties = GetPropertyInfo(asType); // We will parse this until we can no longer parse while (r.Read()) { switch (r.TokenType) { // The current reader is at an end of object case JsonToken.EndObject: if (depth == r.Depth) { return(retVal); } else { throw new JsonSerializationException("JSON in invalid state"); } // Current reader is at a property name case JsonToken.PropertyName: switch ((String)r.Value) { case "$type": var xsiType = s_binder.BindToType(asType.GetTypeInfo().Assembly.FullName, r.ReadAsString()); if (xsiType != asType) { var fmtr = context.JsonContext.GetFormatter(xsiType); var nRetVal = fmtr.Deserialize(r, xsiType, context); nRetVal.CopyObjectData(retVal); return(nRetVal); } break; case "$ref": // TODO: lookup reference break; default: string propertyName = r.Value as String; PropertyInfo propertyInfo = null; if (properties.TryGetValue(propertyName, out propertyInfo)) { // Read next token r.Read(); var instance = context.JsonContext.ReadElementUtil(r, propertyInfo.PropertyType, new JsonSerializationContext(propertyName, context.JsonContext, retVal, context)); if (!(instance == null || (instance as IList)?.Count == 0)) { propertyInfo.SetValue(retVal, instance); } } else { r.Skip(); } break; } break; } } return(retVal); }
/// <summary> /// Creates a new JSON serialization context /// </summary> public JsonSerializationContext(String propertyName, JsonViewModelSerializer context, Object instance, JsonSerializationContext parent) : base(propertyName, context, instance, parent) { }
/// <summary> /// Serialize the specified object to the wire /// </summary> public void Serialize(JsonWriter w, IdentifiedData o, JsonSerializationContext context) { if (o == null) { throw new ArgumentNullException(nameof(o)); } // For each item in the property ... bool loadedProperties = false; // Iterate properties foreach (var propertyInfo in o.GetType().GetRuntimeProperties()) { // Get the property name var propertyName = GetPropertyName(propertyInfo); if (propertyName == null || propertyName.StartsWith("$")) // Skip internal property names { continue; } // Serialization decision if (!context.ShouldSerialize(propertyName)) { continue; } // Get the property var value = propertyInfo.GetValue(o); // Null ,do we want to force load? if (value == null || (value as IList)?.Count == 0) { var tkey = o.Key.HasValue ? o.Key.Value : Guid.NewGuid(); if (context.ShouldForceLoad(propertyName, tkey)) { if (value is IList && !propertyInfo.PropertyType.IsArray) { if (o.Key.HasValue) { value = context.JsonContext.LoadCollection(propertyInfo.PropertyType, (Guid)o.Key); } propertyInfo.SetValue(o, value); loadedProperties = (value as IList).Count > 0; } else { var keyPropertyRef = propertyInfo.GetCustomAttribute <SerializationReferenceAttribute>(); if (keyPropertyRef != null) { var keyProperty = o.GetType().GetRuntimeProperty(keyPropertyRef.RedirectProperty); var key = keyProperty.GetValue(o); if (key != null) { value = context.JsonContext.LoadRelated(propertyInfo.PropertyType, (Guid)key); propertyInfo.SetValue(o, value); loadedProperties = value != null; } } } } else { continue; } } // TODO: Classifier context.JsonContext.WritePropertyUtil(w, propertyName, value, context); } // Loaded something, let's cache it if (loadedProperties && o.Key.HasValue && ((o as IVersionedEntity) == null || (o as IVersionedEntity)?.VersionKey.HasValue == true) && o.LoadState != LoadState.New) { (ApplicationServiceContext.Current.GetService(typeof(IDataCachingService)) as IDataCachingService).Add(o); } }