using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Framework.Test")]
namespace Framework.Json
{
using Framework.DataAccessLayer;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Text.Json;
///
/// By default, properties and fields are serialized to jsonSession (for server session store) and jsonClient (for Angular client).
/// References to ComponentJson are not sent to client by default. But can be enabled exclusively with serialize client attribute.
/// If ComponentJson.IsHidden is true, it is not sent to jsonClient.
///
[Flags]
internal enum SerializeEnum
{
///
/// Do not send property or field to client and do not store in session.
///
None = 0,
///
/// Store property or field value in session on server.
///
Session = 1,
///
/// Send property or field to client. Use for example for render output.
///
Client = 2,
///
/// Send property or field to client and do store in session. Attribute can be omitted.
///
Both = 3,
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
internal class SerializeAttribute : Attribute
{
public SerializeAttribute(SerializeEnum serializeEnum)
{
this.SerializeEnum = serializeEnum;
}
public readonly SerializeEnum SerializeEnum;
}
///
/// Write session and client json. See method Return(); for jsonSession and jsonClient return values.
///
internal sealed class Writer : IDisposable
{
public Writer(JsonWriterOptions options)
{
this.streamSession = new MemoryStream();
this.writerSession = new Utf8JsonWriter(streamSession, options);
this.streamClient = new MemoryStream();
this.writerClient = new Utf8JsonWriter(streamClient, options);
}
private Stack<(bool? IsSerializeSession, bool? IsSerializeClient)> serializeStack = new Stack<(bool? isSerializeSession, bool? isSerializeClient)>();
public int SerializeStackCount
{
get
{
return serializeStack.Count;
}
}
private int? stackRootIndexSession;
private int? stackRootIndexClient;
///
/// Make sure max one object is written to root.
///
public void StackRootValidate()
{
if (IsSerializeSession)
{
if (stackRootIndexSession == null)
{
stackRootIndexSession = serializeStack.Count - 1;
}
else
{
if (stackRootIndexSession.Value == serializeStack.Count - 1)
{
throw new Exception("JsonSession can only have one ComponentJson graph!");
}
}
}
if (IsSerializeClient)
{
if (stackRootIndexClient == null)
{
stackRootIndexClient = serializeStack.Count - 1;
}
else
{
if (stackRootIndexClient.Value == serializeStack.Count - 1)
{
throw new Exception("JsonClient can only have one ComponentJson graph!");
}
}
}
}
///
/// Gets IsSerializeSession. If true, writer writes to jsonSession. See also method SerializeStart();
///
public bool IsSerializeSession { get; private set; }
///
/// Gets IsSerializeClient. If true, writer writes to jsonClient. See also method SerializeStart();
///
public bool IsSerializeClient { get; private set; }
///
/// Configure writer. Method SerializeStart(); has to be followed by method SerializeEnd();
///
public void SerializeStart(bool? isSerializeSession, bool? isSerializeClient)
{
var result = (IsSerializeSession: (bool?)null, IsSerializeClient: (bool?)null);
if (serializeStack.TryPeek(out var serialize))
{
result = serialize;
}
if (isSerializeSession != null && result.IsSerializeSession != false)
{
result.IsSerializeSession = isSerializeSession;
}
if (isSerializeClient != null && result.IsSerializeClient != false)
{
result.IsSerializeClient = isSerializeClient;
}
serializeStack.Push(result);
IsSerializeSession = result.IsSerializeSession == true;
IsSerializeClient = result.IsSerializeClient == true;
}
public void SerializeEnd()
{
serializeStack.Pop();
if (serializeStack.TryPeek(out var result))
{
IsSerializeSession = result.IsSerializeSession == true;
IsSerializeClient = result.IsSerializeClient == true;
}
}
private MemoryStream streamSession;
private readonly Utf8JsonWriter writerSession;
private MemoryStream streamClient;
private readonly Utf8JsonWriter writerClient;
public void WritePropertyName(string propertyName)
{
if (IsSerializeSession)
writerSession.WritePropertyName(propertyName);
if (IsSerializeClient)
writerClient.WritePropertyName(propertyName);
}
public void WriteNumber(string propertyName, int value)
{
if (IsSerializeSession)
writerSession.WriteNumber(propertyName, value);
if (IsSerializeClient)
writerClient.WriteNumber(propertyName, value);
}
public void WriteNull(string propertyName)
{
if (IsSerializeSession)
writerSession.WriteNull(propertyName);
if (IsSerializeClient)
writerClient.WriteNull(propertyName);
}
public void WriteStartObject()
{
if (IsSerializeSession)
writerSession.WriteStartObject();
if (IsSerializeClient)
writerClient.WriteStartObject();
}
public void WriteStartArray()
{
if (IsSerializeSession)
writerSession.WriteStartArray();
if (IsSerializeClient)
writerClient.WriteStartArray();
}
public void WriteEndArray()
{
if (IsSerializeSession)
writerSession.WriteEndArray();
if (IsSerializeClient)
writerClient.WriteEndArray();
}
public void WriteEndObject()
{
if (IsSerializeSession)
writerSession.WriteEndObject();
if (IsSerializeClient)
writerClient.WriteEndObject();
}
public void WriteNullValue()
{
if (IsSerializeSession)
writerSession.WriteNullValue();
if (IsSerializeClient)
writerClient.WriteNullValue();
}
public void WriteNumberValue(int value)
{
if (IsSerializeSession)
writerSession.WriteNumberValue(value);
if (IsSerializeClient)
writerClient.WriteNumberValue(value);
}
internal void WriteNumberValue(double value)
{
if (IsSerializeSession)
writerSession.WriteNumberValue(value);
if (IsSerializeClient)
writerClient.WriteNumberValue(value);
}
public void WriteStringValue(string value)
{
if (IsSerializeSession)
writerSession.WriteStringValue(value);
if (IsSerializeClient)
writerClient.WriteStringValue(value);
}
public void WriteBooleanValue(bool value)
{
if (IsSerializeSession)
writerSession.WriteBooleanValue(value);
if (IsSerializeClient)
writerClient.WriteBooleanValue(value);
}
public void WriteString(string propertyName, string value)
{
if (IsSerializeSession)
writerSession.WriteString(propertyName, value);
if (IsSerializeClient)
writerClient.WriteString(propertyName, value);
}
internal void Serialize(object value)
{
if (IsSerializeSession)
JsonSerializer.Serialize(writerSession, value, value.GetType());
if (IsSerializeClient)
JsonSerializer.Serialize(writerClient, value, value.GetType());
}
public void Return(out string jsonSession, out string jsonClient)
{
UtilFramework.Assert(serializeStack.Count == 0);
writerSession.Flush();
writerClient.Flush();
jsonSession = Encoding.UTF8.GetString(streamSession.ToArray());
jsonClient = Encoding.UTF8.GetString(streamClient.ToArray());
}
public void Dispose()
{
writerClient.Dispose();
streamClient.Dispose();
writerSession.Dispose();
streamSession.Dispose();
}
}
internal static class UtilJson
{
///
/// (TypeName, DeclarationObject)
///
private static readonly ConcurrentDictionary declarationObjectList = new ConcurrentDictionary();
private static DeclarationObject DeclarationObjectGet(string typeName)
{
return declarationObjectList.GetOrAdd(typeName, (key) =>
{
Type type = Type.GetType(typeName);
bool isComponentJson = UtilFramework.IsSubclassOf(type, typeof(ComponentJson));
bool isRow = UtilFramework.IsSubclassOf(type, typeof(Row));
bool isDto = type.Assembly == typeof(UtilFramework).Assembly; // Dto declared in UtilFramework
isDto = isDto || type.Namespace.StartsWith("Framework.Test"); // Dto declared in Framework.Test
UtilFramework.Assert(isComponentJson | isRow | isDto);
return new DeclarationObject(type);
});
}
private static DeclarationObject DeclarationObjectGet(Type type)
{
return DeclarationObjectGet(UtilFramework.TypeToName(type, true));
}
private class DeclarationObject
{
public DeclarationObject(Type type)
{
this.Type = type;
this.TypeName = type.FullName;
// Property
foreach (var propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
if (propertyInfo.CanWrite) // If property has no setter do not add it.
{
if (SerializeAttribute(propertyInfo.GetCustomAttribute(), out bool isSerializeSession, out bool isSerializeClient, out bool isAttribute)) // If SerializeEnum.Both, do not add property
{
DeclarationProperty property = new DeclarationProperty(propertyInfo, isSerializeSession, isSerializeClient, isAttribute);
PropertyList.Add(property.PropertyName, property);
}
}
}
// Field
foreach (var fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
if (SerializeAttribute(fieldInfo.GetCustomAttribute(), out bool isSerializeSession, out bool isSerializeClient, out bool isAttribute)) // If SerializeEnum.Both, do not add property
{
if (fieldInfo.Attributes != FieldAttributes.Private)
{
DeclarationProperty property = new DeclarationProperty(fieldInfo, isSerializeSession, isSerializeClient, isAttribute);
PropertyList.Add(property.PropertyName, property);
}
}
}
}
private bool SerializeAttribute(SerializeAttribute attribute, out bool isSerializeSession, out bool isSerializeClient, out bool isAttribute)
{
SerializeEnum serializeEnum = SerializeEnum.Both;
isAttribute = false;
if (attribute != null)
{
isAttribute = true;
serializeEnum = attribute.SerializeEnum;
}
isSerializeSession = (serializeEnum & SerializeEnum.Session) > 0;
isSerializeClient = (serializeEnum & SerializeEnum.Client) > 0;
return (serializeEnum != SerializeEnum.None);
}
public readonly Type Type;
public readonly string TypeName;
///
/// (PropertyName, DeclarationProperty).
///
public Dictionary PropertyList = new Dictionary();
}
internal class DeclarationProperty
{
public DeclarationProperty(PropertyInfo propertyInfo, bool isSerializeSession, bool isSerializeClient, bool isAttribute)
{
this.PropertyInfo = propertyInfo;
this.PropertyName = propertyInfo.Name;
this.PropertyType = propertyInfo.PropertyType;
this.IsSerializeSession = isSerializeSession;
this.IsSerializeClient = isSerializeClient;
this.IsAttribute = isAttribute;
this.IsSerializeClientExclusive = IsSerializeClient && IsAttribute;
Constructor(ref this.PropertyType, ref this.IsList);
this.Converter = ConverterGet(this.PropertyType);
}
public DeclarationProperty(FieldInfo fieldInfo, bool isSerializeSession, bool isSerializeClient, bool isAttribute)
{
this.FieldInfo = fieldInfo;
this.PropertyName = fieldInfo.Name;
this.PropertyType = fieldInfo.FieldType;
this.IsSerializeSession = isSerializeSession;
this.IsSerializeClient = isSerializeClient;
this.IsAttribute = isAttribute;
this.IsSerializeClientExclusive = IsSerializeClient && IsAttribute;
Constructor(ref this.PropertyType, ref this.IsList);
this.Converter = ConverterGet(this.PropertyType);
}
private void Constructor(ref Type propertyType, ref bool isList)
{
if (propertyType.IsGenericType && (propertyType.GetGenericTypeDefinition() == typeof(List<>) || propertyType.GetGenericTypeDefinition() == typeof(IReadOnlyList<>)))
{
isList = true;
propertyType = propertyType.GetGenericArguments()[0];
}
if (propertyType.IsArray)
{
isList = true;
propertyType = this.PropertyType.GetElementType();
}
}
public readonly FieldInfo FieldInfo;
public readonly PropertyInfo PropertyInfo;
public readonly string PropertyName;
///
/// Gets PropertyType. Declaring type for property and list.
///
public readonly Type PropertyType;
public readonly ConverterBase Converter;
///
/// Gets IsList. Field is either single field or list.
///
public readonly bool IsList;
public readonly bool IsSerializeSession;
public readonly bool IsSerializeClient;
///
/// Gets IsSerializeClientExclusive. If true, property has client serialization attribute declared exclusively.
///
public readonly bool IsSerializeClientExclusive;
///
/// Gets IsAttribute. If true, serialize attribute is explicitly declared on this property.
///
public readonly bool IsAttribute;
public object ValueGet(object obj)
{
UtilFramework.Assert(this.IsList == false);
object result;
if (PropertyInfo != null)
{
result = PropertyInfo.GetValue(obj);
}
else
{
result = FieldInfo.GetValue(obj);
// TypedReference typedReference = __makeref(obj);
// result = FieldInfo.GetValueDirect(typedReference);
}
return result;
}
public void ValueSet(object obj, object value)
{
UtilFramework.Assert(this.IsList == false);
if (PropertyInfo != null)
{
PropertyInfo.SetValue(obj, value);
}
else
{
FieldInfo.SetValue(obj, value);
}
}
public IList ValueListGet(object obj)
{
UtilFramework.Assert(this.IsList == true);
IList result;
if (PropertyInfo != null)
{
result = (IList)PropertyInfo.GetValue(obj);
}
else
{
result = (IList)FieldInfo.GetValue(obj);
}
return result;
}
public void ValueListSet(object obj, IList valueList)
{
UtilFramework.Assert(this.IsList == true);
if (PropertyInfo?.PropertyType.IsArray == true || FieldInfo?.FieldType.IsArray == true)
{
var valueListArray = Array.CreateInstance(PropertyType, valueList.Count);
valueList.CopyTo(valueListArray, 0);
valueList = valueListArray;
}
if (PropertyInfo != null)
{
PropertyInfo.SetValue(obj, valueList);
}
else
{
FieldInfo.SetValue(obj, valueList);
}
}
}
///
/// (PropertyType, Converter)
///
private static readonly ConcurrentDictionary converterList = new ConcurrentDictionary(new KeyValuePair[] {
// Value types
new KeyValuePair(new ConverterInt().PropertyType, new ConverterInt()),
new KeyValuePair(new ConverterIntNullable().PropertyType, new ConverterIntNullable()),
new KeyValuePair(new ConverterBoolean().PropertyType, new ConverterBoolean()),
new KeyValuePair(new ConverterBooleanNullable().PropertyType, new ConverterBooleanNullable()),
new KeyValuePair(new ConverterDouble().PropertyType, new ConverterDouble()),
new KeyValuePair(new ConverterDoubleNullable().PropertyType, new ConverterDoubleNullable()),
new KeyValuePair(new ConverterDateTime().PropertyType, new ConverterDateTime()),
new KeyValuePair(new ConverterDateTimeNullable().PropertyType, new ConverterDateTimeNullable()),
new KeyValuePair(new ConverterString().PropertyType, new ConverterString()),
// Special types
new KeyValuePair(new ConverterObjectValue().PropertyType, new ConverterObjectValue()), // Value object
new KeyValuePair(new ConverterType().PropertyType, new ConverterType()), // Type
});
private static readonly ConverterObjectRoot converterObjectRoot = new ConverterObjectRoot();
private static readonly ConverterObjectDto converterObjectDto = new ConverterObjectDto();
private static readonly ConverterObjectRow converterObjectRow = new ConverterObjectRow();
private static readonly ConverterObjectComponentJson converterObjectComponentJson = new ConverterObjectComponentJson();
private static readonly ConverterEnum converterEnum = new ConverterEnum();
private static readonly ConverterEnumNullable converterEnumNullable = new ConverterEnumNullable();
///
/// (Type, typeof(<PropertyType>))
///
private static readonly ConcurrentDictionary TypeGenericList = new ConcurrentDictionary();
///
/// Returns Converter.
///
/// Property type
private static ConverterBase ConverterGet(Type propertyType)
{
if (!converterList.TryGetValue(propertyType, out ConverterBase result)) // Value type
if (propertyType.IsEnum) // Emum
result = converterEnum;
else
if (UtilFramework.TypeUnderlying(propertyType).IsEnum) // EnumNullable
result = converterEnumNullable;
else
if (UtilFramework.IsSubclassOf(propertyType, typeof(Row))) // Row
result = converterObjectRow;
else
if (UtilFramework.IsSubclassOf(propertyType, typeof(ComponentJson))) // ComponentJson
result = converterObjectComponentJson;
else
if (propertyType.Assembly == typeof(UtilFramework).Assembly || propertyType.Namespace.StartsWith("Framework.Test")) // Dto
result = converterObjectDto;
UtilFramework.Assert(result != null, "Type not supported!");
return result;
}
internal abstract class ConverterBase
{
public ConverterBase(Type propertyType, Type propertyTypeGeneric, bool isObject, object valueDefault)
{
this.PropertyType = propertyType;
this.propertyTypeGeneric = propertyTypeGeneric;
this.IsObject = isObject;
this.ValueDefault = valueDefault;
UtilFramework.Assert(!(this.PropertyType == null ^ this.propertyTypeGeneric == null));
}
public ConverterBase(bool isObject, object valueDefault)
: this(null, null, isObject, valueDefault)
{
}
protected virtual bool IsValueDefault(object value)
{
return object.Equals(value, ValueDefault);
}
protected virtual void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
{
// writer.WriteStringValue(string.Format("{0}", value));
}
protected virtual void SerializeObjectType(DeclarationProperty property, object obj, Writer writer)
{
// writer.WriteString("$typeRoot", UtilFramework.TypeToName(obj.GetType()));
}
///
/// Returns true if 'property' and 'value' is a reference to a ComponentJson and serializes it to jsonSession.
///
private bool ReferenceSerialize(object obj, DeclarationProperty property, object value, ref ComponentJson componentJsonRoot, Writer writer)
{
bool result = false;
if (value is ComponentJson valueComponentJson)
{
if (obj is ComponentJson objComponentJson)
{
if (property.PropertyName == nameof(ComponentJson.List))
{
// ComponentJson.List
}
else
{
// ComponentJson.Property
result = true;
}
}
else
{
if (componentJsonRoot == null)
{
UtilFramework.Assert(valueComponentJson.Owner == null, "Referenced ComponentJson not root!");
componentJsonRoot = valueComponentJson;
}
else
{
// Dto referenced ComponentJson in object graph.
result = true;
}
}
if (result)
{
int? id = valueComponentJson.Id;
if (valueComponentJson.IsRemoved)
{
// UtilFramework.Assert(false, string.Format("Reference to removed ComponentJson! ({0}.{1})", UtilFramework.TypeToName(obj.GetType()), property.PropertyName));
id = null;
}
UtilFramework.Assert(valueComponentJson.Root == componentJsonRoot, "Referenced ComponentJson not in same object graph!");
result = true;
writer.SerializeStart(null, false); // Do not serialize reference to client
writer.WriteStartObject();
if (id != null)
{
writer.WriteNumber("$referenceId", id.Value);
}
else
{
writer.WriteNull("$referenceId");
}
writer.WriteEndObject();
writer.SerializeEnd();
}
}
return result;
}
private void SerializeObject(object obj, DeclarationProperty property, object value, ComponentJson componentJsonRoot, Writer writer)
{
bool isReference = ReferenceSerialize(obj, property, value, ref componentJsonRoot, writer);
bool isSerializeClientExclusive = (property?.IsSerializeClientExclusive).GetValueOrDefault();
if (isReference && !isSerializeClientExclusive)
{
return;
}
DeclarationObject declarationObject;
declarationObject = DeclarationObjectGet(value.GetType());
bool? isSerializeSessionObject = isReference && isSerializeClientExclusive ? false : (bool?)null; // Session reference has already been serialized by method ReferenceSerialize();
bool? isSerializeClientObject = (bool?)isSerializeClientExclusive | ((value is ComponentJson) ? true : (bool?)null); // Serialize to client if client attribute is declared on property.
writer.SerializeStart(isSerializeSessionObject, isSerializeClientObject);
writer.StackRootValidate();
writer.WriteStartObject();
SerializeObjectType(property, value, writer);
foreach (var valueProperty in declarationObject.PropertyList.Values)
{
ConverterBase converter = valueProperty.Converter;
bool? isSerializeClient = null;
bool? isSerializeSession = null;
if (converter is ConverterObjectRow)
{
isSerializeClient = false; // Do not send data row to client.
}
if (converter is ConverterObjectComponentJson)
{
if (!(value is ComponentJson)) // Property references ComponentJson
{
if (writer.IsSerializeClient && !valueProperty.IsSerializeClientExclusive)
{
isSerializeClient = false; // Do not send ComponentJson reference to client.
}
}
if (value is ComponentJson && valueProperty.PropertyName != nameof(ComponentJson.List)) // ComponentJson references ComponentJson
{
isSerializeClient = false; // Do not send ComponentJson reference to client.
}
}
// SerializeAttribute
if (valueProperty.IsSerializeSession == false)
{
isSerializeSession = false;
}
if (valueProperty.IsSerializeClient == false)
{
isSerializeClient = false;
}
// Serialize property, list
if (valueProperty.IsList == false)
{
// Serialize property
object propertyValue = valueProperty.ValueGet(value);
if (!converter.IsValueDefault(propertyValue))
{
if (propertyValue is ComponentJson componentJson && componentJson.IsHide && !valueProperty.IsSerializeClientExclusive)
{
isSerializeClient = false; // No list entry for hidden object.
}
writer.SerializeStart(isSerializeSession, isSerializeClient);
writer.WritePropertyName(valueProperty.PropertyName);
converter.Serialize(value, valueProperty, propertyValue, componentJsonRoot, writer);
writer.SerializeEnd();
}
}
else
{
// Serialize list
IList propertyValueList = valueProperty.ValueListGet(value);
if (propertyValueList?.Count > 0)
{
writer.WritePropertyName(valueProperty.PropertyName);
writer.WriteStartArray();
foreach (var propertyValue in propertyValueList)
{
var isSerializeClientLocal = isSerializeClient;
if (propertyValue is IHide hide && hide.IsHide)
{
isSerializeClientLocal = false; // No list entry for hidden object.
}
writer.SerializeStart(isSerializeSession, isSerializeClientLocal);
if (!converter.IsValueDefault(propertyValue))
{
converter.Serialize(value, valueProperty, propertyValue, componentJsonRoot, writer);
}
else
{
if (converter.ValueDefault == null)
{
writer.WriteNullValue(); // Serialize null
}
else
{
converter.Serialize(value, valueProperty, propertyValue, componentJsonRoot, writer);
}
}
writer.SerializeEnd();
}
writer.WriteEndArray();
}
}
}
writer.WriteEndObject();
writer.SerializeEnd();
}
internal void Serialize(object obj, DeclarationProperty property, object value, ComponentJson componentJsonRoot, Writer writer)
{
if (IsObject == false)
{
SerializeValue(obj, property, value, writer);
}
else
{
SerializeObject(obj, property, value, componentJsonRoot, writer);
}
}
protected virtual object Utf8JsonReaderDeserializeValue(object obj, DeclarationProperty property, Utf8JsonReader reader)
{
throw new NotImplementedException(); // return reader.GetString();
}
protected virtual object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
{
throw new NotImplementedException(); // return jsonElement.GetString();
}
protected virtual string DeserializeObjectType(DeclarationProperty property, JsonElement jsonElement)
{
// return jsonElement.GetProperty("$typeRoot").GetString();
return null;
}
protected virtual string Utf8JsonReaderDeserializeObjectType(DeclarationProperty property, Utf8JsonReader reader)
{
reader.Read();
UtilFramework.Assert(reader.TokenType == JsonTokenType.PropertyName);
string propertyName = reader.GetString();
UtilFramework.Assert(propertyName == "$typeRoot");
reader.Read();
UtilFramework.Assert(reader.TokenType == JsonTokenType.String);
string result = reader.GetString();
return result;
}
private object Utf8JsonReaderDeserializeObject(DeclarationProperty property, Utf8JsonReader reader)
{
reader.Read();
UtilFramework.Assert(reader.TokenType == JsonTokenType.StartObject);
Type type = PropertyType;
string typeName = Utf8JsonReaderDeserializeObjectType(property, reader);
if (typeName != null)
{
type = UtilFramework.TypeFromName(typeName);
}
var result = FormatterServices.GetUninitializedObject(type);
var declarationObject = DeclarationObjectGet(type);
return result;
}
private bool ReferenceDeserialize(object obj, DeclarationProperty property, JsonElement jsonElement, out object result, ComponentJson componentJsonRoot)
{
bool resultReturn = false;
result = null;
if (jsonElement.TryGetProperty("$referenceId", out JsonElement jsonElementReference))
{
if (jsonElementReference.ValueKind != JsonValueKind.Null)
{
int id = jsonElementReference.GetInt32();
componentJsonRoot.RootReferenceList.Add((obj, property, id)); // Register reference to solve later.
}
resultReturn = true;
}
return resultReturn;
}
private void DeserializeObjectComponentJsonConstructor(object obj, DeclarationProperty property, object value)
{
if (value is ComponentJson componentJson)
{
ComponentJson owner = null;
if (obj is ComponentJson && property.PropertyName == nameof(ComponentJson.List))
{
owner = (ComponentJson)obj;
}
componentJson.Constructor(owner, isDeserialize: true);
}
}
private object DeserializeObject(object obj, DeclarationProperty property, JsonElement jsonElement, ComponentJson componentJsonRoot)
{
if (ReferenceDeserialize(obj, property, jsonElement, out object result, componentJsonRoot))
{
return result;
}
Type type = PropertyType;
string typeName = DeserializeObjectType(property, jsonElement);
if (typeName != null)
{
type = UtilFramework.TypeFromName(typeName);
}
if (type == null)
{
type = property.PropertyType; // Dto
}
result = FormatterServices.GetUninitializedObject(type);
DeserializeObjectComponentJsonConstructor(obj, property, result);
bool isReferenceSolve = false;
if (result is ComponentJson componentJson && componentJson.Owner == null)
{
componentJsonRoot = componentJson;
isReferenceSolve = true;
}
var declarationObject = DeclarationObjectGet(type);
// Create empty lists.
Dictionary valueListList = new Dictionary();
foreach (var item in declarationObject.PropertyList.Values)
{
if (item.IsList)
{
Type typeGeneric = item.Converter.PropertyTypeGeneric(item);
IList valueList = (IList)Activator.CreateInstance(typeGeneric);
valueListList.Add(item.PropertyName, valueList);
item.ValueListSet(result, valueList);
}
}
// Loop through json properties
foreach (var jsonElementProperty in jsonElement.EnumerateObject())
{
string propertyName = jsonElementProperty.Name;
if (declarationObject.PropertyList.TryGetValue(propertyName, out var item)) // Could be "$type"
{
if (item.IsList == false)
{
object value = item.Converter.Deserialize(result, item, jsonElementProperty.Value, componentJsonRoot);
item.ValueSet(result, value);
}
else
{
IList valueList = valueListList[item.PropertyName];
foreach (var jsonElementValue in jsonElementProperty.Value.EnumerateArray())
{
object value = null;
if (jsonElementValue.ValueKind == JsonValueKind.Null)
{
UtilFramework.Assert(item.Converter.ValueDefault == null);
}
else
{
value = item.Converter.Deserialize(result, item, jsonElementValue, componentJsonRoot);
}
valueList.Add(value);
}
item.ValueListSet(result, valueList);
}
}
}
if (result is ComponentJson resultComponentJson)
{
resultComponentJson.Root.RootComponentJsonList.Add(resultComponentJson.Id, resultComponentJson);
}
if (isReferenceSolve)
{
componentJsonRoot.RootReferenceSolve();
}
return result;
}
///
/// Deserialize value or object.
///
/// Object on which property is declared.
/// Property of object
internal object Deserialize(object obj, DeclarationProperty property, JsonElement jsonElement, ComponentJson componentJsonRoot)
{
if (IsObject == false)
{
return DeserializeValue(obj, property, jsonElement);
}
else
{
return DeserializeObject(obj, property, jsonElement, componentJsonRoot);
}
}
internal object Utf8JsonReaderDeserialize(object obj, DeclarationProperty property, Utf8JsonReader reader)
{
if (IsObject == false)
{
return Utf8JsonReaderDeserializeValue(obj, property, reader);
}
else
{
return Utf8JsonReaderDeserializeObject(property, reader);
}
}
///
/// Gets PropertyType. Declaring type. Can be null for example for Enum, Dto, Row and ComponentJson.
///
public readonly Type PropertyType;
///
/// Gets PropertyTypeGeneric. This is typeof(<PropertyType>).
///
private readonly Type propertyTypeGeneric;
public Type PropertyTypeGeneric(DeclarationProperty property)
{
var result = propertyTypeGeneric;
if (result == null)
{
result = TypeGenericList.GetOrAdd(property.PropertyType, (Type type) => typeof(List<>).MakeGenericType(property.PropertyType));
}
return result;
}
///
/// Gets IsObject. If false, it is a value type. If true it is an object.
///
public readonly bool IsObject;
///
/// Gets ValueDefault. Used to ignore default values.
///
public readonly object ValueDefault;
}
private abstract class ConverterBase : ConverterBase
{
public ConverterBase(bool isObject)
: base(typeof(T), typeof(List), isObject, default(T))
{
}
}
private sealed class ConverterInt : ConverterBase
{
public ConverterInt()
: base(false)
{
}
protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
{
writer.WriteNumberValue((int)value);
}
protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
{
return jsonElement.GetInt32();
}
}
private sealed class ConverterIntNullable : ConverterBase
{
public ConverterIntNullable()
: base(false)
{
}
protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
{
writer.WriteNumberValue((int)value);
}
protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
{
return jsonElement.GetInt32();
}
}
private sealed class ConverterString : ConverterBase
{
public ConverterString()
: base(false)
{
}
protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
{
writer.WriteStringValue((string)value);
}
protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
{
return jsonElement.GetString();
}
}
private sealed class ConverterBoolean : ConverterBase
{
public ConverterBoolean()
: base(false)
{
}
protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
{
writer.WriteBooleanValue((bool)value);
}
protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
{
return jsonElement.GetBoolean();
}
}
private sealed class ConverterBooleanNullable : ConverterBase
{
public ConverterBooleanNullable()
: base(false)
{
}
protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
{
writer.WriteBooleanValue((bool)value);
}
protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
{
return jsonElement.GetBoolean();
}
}
private sealed class ConverterDouble : ConverterBase
{
public ConverterDouble()
: base(false)
{
}
protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
{
writer.WriteNumberValue((double)value);
}
protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
{
return jsonElement.GetDouble();
}
}
private sealed class ConverterDoubleNullable : ConverterBase
{
public ConverterDoubleNullable()
: base(false)
{
}
protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
{
writer.WriteNumberValue((double)value);
}
protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
{
return jsonElement.GetDouble();
}
}
private sealed class ConverterDateTime : ConverterBase
{
public ConverterDateTime()
: base(false)
{
}
protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
{
writer.WriteStringValue(UtilFramework.DateTimeToText((DateTime)value));
}
protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
{
return UtilFramework.DateTimeFromText(jsonElement.GetString());
}
}
private sealed class ConverterDateTimeNullable : ConverterBase
{
public ConverterDateTimeNullable()
: base(false)
{
}
protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
{
writer.WriteStringValue(UtilFramework.DateTimeToText((DateTime?)value));
}
protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
{
return UtilFramework.DateTimeFromText(jsonElement.GetString());
}
}
private sealed class ConverterType : ConverterBase
{
public ConverterType()
: base(false)
{
}
protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
{
string typeName = UtilFramework.TypeToName((Type)value, true);
writer.WriteStringValue(typeName);
}
protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
{
string typeName = jsonElement.GetString();
var result = UtilFramework.TypeFromName(typeName);
return result;
}
}
private sealed class ConverterObjectRow : ConverterBase
{
public ConverterObjectRow()
: base(false, null)
{
}
protected override void SerializeValue(object obj, DeclarationProperty property, object value, Writer writer)
{
if (writer.IsSerializeClient)
{
throw new Exception("Can not send data row to client!");
}
writer.WriteStartObject();
writer.WriteString("$typeRow", UtilFramework.TypeToName(value.GetType(), true));
writer.WritePropertyName("Row");
writer.Serialize(value);
writer.WriteEndObject();
}
protected override object DeserializeValue(object obj, DeclarationProperty property, JsonElement jsonElement)
{
string typeRowName = jsonElement.GetProperty("$typeRow").GetString();
Type typeRow = UtilFramework.TypeFromName(typeRowName);
string json = jsonElement.GetProperty("Row").GetRawText();
var result = JsonSerializer.Deserialize(json, typeRow);
return result;
}
}
private sealed class ConverterObjectComponentJson : ConverterBase
{
public ConverterObjectComponentJson()
: base(true, null)
{
}
protected override void SerializeObjectType(DeclarationProperty property, object obj, Writer writer)
{
writer.SerializeStart(null, false);
writer.WriteString("$typeComponent", UtilFramework.TypeToName(obj.GetType(), true));
writer.SerializeEnd();
}
protected override string DeserializeObjectType(DeclarationProperty property, JsonElement jsonElement)
{
return jsonElement.GetProperty("$typeComponent").GetString();
}
}
///
/// Serialize an object. Property type is object. Supports inheritance.
///
private sealed class ConverterObjectValue : ConverterBase