/// <summary> /// PropertyType has to be (ComponentJson, Row or Type). Or PropertyType and PropertyValue type need to match. Applies also for list or dictionary. /// </summary> private void ValidatePropertyAndValueType(PropertyInfo propertyInfo, object propertyValue) { var property = new Property(propertyInfo, propertyValue); Type propertyType = property.PropertyType; ICollection propertyValueList = property.PropertyValueList; // Property type is of type ComponentJson or Row. For example property type object would throw exception. if (UtilFramework.IsSubclassOf(propertyType, typeof(ComponentJson)) || UtilFramework.IsSubclassOf(propertyType, typeof(Row))) { return; } // Property type is class Type. Property value typeof(int) is class RuntimeType (which derives from class Type) if (propertyType == typeof(Type)) { return; } foreach (var item in propertyValueList) { if (item != null) { // Property type is equal to value. No inheritance. if (!(UtilFramework.TypeUnderlying(propertyType) == item.GetType())) { throw new Exception(string.Format("Combination property type and value type not supported! (PropertyName={0}.{1}; PropertyType={2}; ValueType={3}; Value={4};)", propertyInfo.DeclaringType.Name, propertyInfo.Name, propertyType.Name, item.GetType().Name, item)); } } } }
/// <summary> /// Deserialize ComponentJson or Row objects. /// </summary> public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // Deserialize Type object if (typeToConvert == typeof(Type)) { var typeName = JsonSerializer.Deserialize <string>(ref reader); return((T)(object)Type.GetType(typeName)); } // Deserialize ComponentJson or Row object var valueList = JsonSerializer.Deserialize <Dictionary <string, JsonElement> >(ref reader); // Deserialize Row if (UtilFramework.IsSubclassOf(typeToConvert, typeof(Row))) { var typeRowName = valueList["$typeRow"].GetString(); string rowJson = valueList["$row"].GetRawText(); Type typeRow = Type.GetType(typeRowName); var resultRow = JsonSerializer.Deserialize(rowJson, typeRow); // Native deserialization for data row. return((T)(object)resultRow); } // Read type information string typeText = valueList["$type"].GetString(); Type type = Type.GetType(typeText); // TODO Cache on factory var result = (ComponentJson)Activator.CreateInstance(type); // TODO No parameterless constructor for ComponentJson // Loop through properties foreach (var propertyInfo in type.GetProperties()) { if (valueList.ContainsKey(propertyInfo.Name)) { if (IsComponentJsonReference(propertyInfo)) { // Deserialize ComponentJsonReference var componentJsonReferenceValueList = JsonSerializer.Deserialize <Dictionary <string, JsonElement> >(valueList[propertyInfo.Name].GetRawText()); UtilFramework.Assert(componentJsonReferenceValueList["$type"].GetString() == "$componentJsonReference"); int id = componentJsonReferenceValueList["$id"].GetInt32(); Factory.ComponentJsonReferenceList.Add(new ComponentJsonReference { PropertyInfo = propertyInfo, ComponentJson = result, Id = id }); continue; } // Deserialize ComponentJson var propertyValue = JsonSerializer.Deserialize(valueList[propertyInfo.Name].GetRawText(), propertyInfo.PropertyType, options); propertyInfo.SetValue(result, propertyValue); } } // Add ComponentJson for ComponentJsonReference resolve Factory.ComponentJsonList.Add(result.Id, result); return((T)(object)result); }
private bool IsComponentJsonReference(PropertyInfo propertyInfo) { bool result = false; Property property = new Property(propertyInfo, null); bool isComponentJsonList = propertyInfo.DeclaringType == typeof(ComponentJson) && propertyInfo.Name == nameof(ComponentJson.List); bool isComponentJson = UtilFramework.IsSubclassOf(property.PropertyType, typeof(ComponentJson)); if (!isComponentJsonList && isComponentJson) // Is it a component reference? { if (property.PropertyEnum == PropertyEnum.List || property.PropertyEnum == PropertyEnum.Dictionary) { throw new Exception("ComponentJson reference supported only for property! Not for list and dictionary!"); } result = true; } return(result); }
/// <summary> /// Serialize ComponentJson or Row objects. /// </summary> public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { // Serialize Type object if (typeof(T) == typeof(Type)) { JsonSerializer.Serialize(writer, (value as Type).FullName); return; } // Serialize data row object if (UtilFramework.IsSubclassOf(typeof(T), typeof(Row))) { writer.WriteStartObject(); writer.WritePropertyName("$typeRow"); JsonSerializer.Serialize(writer, value.GetType().FullName); writer.WritePropertyName("$row"); JsonSerializer.Serialize(writer, value, value.GetType()); // Native serialization of data row writer.WriteEndObject(); return; } // ComponentJson or Row object start writer.WriteStartObject(); // Type information writer.WritePropertyName("$type"); // Note: Type information could be omitted if property type is equal to property value type. JsonSerializer.Serialize(writer, value.GetType().FullName); // Loop through properties foreach (var propertyInfo in value.GetType().GetProperties()) { var propertyValue = propertyInfo.GetValue(value); bool isIgnoreNullValue = false; if (propertyValue == null) { isIgnoreNullValue = true; } if (propertyValue is ICollection list && list.Count == 0) { isIgnoreNullValue = true; } if (!isIgnoreNullValue) { ValidatePropertyAndValueType(propertyInfo, propertyValue); writer.WritePropertyName(propertyInfo.Name); if (IsComponentJsonReference(propertyInfo)) { writer.WriteStartObject(); writer.WritePropertyName("$type"); JsonSerializer.Serialize(writer, "$componentJsonReference"); writer.WritePropertyName("$id"); var id = ((ComponentJson)propertyValue).Id; JsonSerializer.Serialize <int>(writer, id); writer.WriteEndObject(); } else { // TODO Distinct serialize for client and for server. // TODO Check ComponentJson reference is in same composition- graph. // Serialize property value JsonSerializer.Serialize(writer, propertyValue, propertyInfo.PropertyType, options); } } } // ComponentJson or Row object end writer.WriteEndObject(); }
public override bool CanConvert(Type typeToConvert) { // Handle inheritance of ComponentJson and Row classes. Or handle Type object. return(UtilFramework.IsSubclassOf(typeToConvert, typeof(ComponentJson)) || UtilFramework.IsSubclassOf(typeToConvert, typeof(Row)) || typeToConvert == typeof(Type)); }