Files
FlaxEngine/Source/Engine/Serialization/JsonCustomSerializers/ExtendedDefaultContractResolver.cs
2023-01-10 15:29:37 +01:00

167 lines
5.6 KiB
C#

// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace FlaxEngine.Json.JsonCustomSerializers
{
internal class ExtendedDefaultContractResolver : DefaultContractResolver
{
private readonly Type _flaxType = typeof(Object);
private readonly Type[] AttributesIgnoreList =
{
typeof(NonSerializedAttribute),
typeof(NoSerializeAttribute)
};
private readonly Type[] AttributesIgnoreListManaged =
{
typeof(UnmanagedAttribute),
typeof(NonSerializedAttribute),
typeof(NoSerializeAttribute)
};
private readonly Type[] _attributesIgnoreList;
public ExtendedDefaultContractResolver(bool isManagedOnly)
{
_attributesIgnoreList = isManagedOnly ? AttributesIgnoreListManaged : AttributesIgnoreList;
}
/// <inheritdoc />
protected override JsonContract CreateContract(Type objectType)
{
var contract = base.CreateContract(objectType);
// Override contract for Flax objects
if (_flaxType.IsAssignableFrom(objectType))
{
((JsonObjectContract)contract).ItemReferenceLoopHandling = ReferenceLoopHandling.Serialize;
}
return contract;
}
/// <inheritdoc />
protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
{
var contract = base.CreateDictionaryContract(objectType);
// Override contract to save enums keys as integer
if (contract.DictionaryKeyType?.IsEnum ?? false)
{
var enumType = contract.DictionaryKeyType;
contract.DictionaryKeyResolver = name =>
{
try
{
var e = Enum.Parse(enumType, name);
name = Convert.ToInt32(e).ToString();
}
catch
{
// Ignore errors
}
return name;
};
}
return contract;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
var result = new List<JsonProperty>(fields.Length + properties.Length);
for (int i = 0; i < fields.Length; i++)
{
var f = fields[i];
var attributes = f.GetCustomAttributes();
// Serialize non-public fields only with a proper attribute
if (!f.IsPublic && !attributes.Any(x => x is SerializeAttribute))
continue;
// Check if has attribute to skip serialization
bool noSerialize = false;
foreach (var attribute in attributes)
{
if (_attributesIgnoreList.Contains(attribute.GetType()))
{
noSerialize = true;
break;
}
}
if (noSerialize)
continue;
var jsonProperty = CreateProperty(f, memberSerialization);
jsonProperty.Writable = true;
jsonProperty.Readable = true;
if (_flaxType.IsAssignableFrom(f.FieldType))
{
jsonProperty.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
jsonProperty.Converter = JsonSerializer.ObjectConverter;
}
result.Add(jsonProperty);
}
for (int i = 0; i < properties.Length; i++)
{
var p = properties[i];
// Serialize only properties with read/write
if (!(p.CanRead && p.CanWrite && p.GetIndexParameters().GetLength(0) == 0))
continue;
var attributes = p.GetCustomAttributes();
// Serialize non-public properties only with a proper attribute
if ((!p.GetMethod.IsPublic || !p.SetMethod.IsPublic) && !attributes.Any(x => x is SerializeAttribute))
continue;
// Check if has attribute to skip serialization
bool noSerialize = false;
foreach (var attribute in attributes)
{
if (_attributesIgnoreList.Contains(attribute.GetType()))
{
noSerialize = true;
break;
}
}
if (noSerialize)
continue;
var isObsolete = attributes.Any(x => x is ObsoleteAttribute);
var jsonProperty = CreateProperty(p, memberSerialization);
jsonProperty.Writable = true;
jsonProperty.Readable = !isObsolete;
if (_flaxType.IsAssignableFrom(p.PropertyType))
{
jsonProperty.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
jsonProperty.Converter = JsonSerializer.ObjectConverter;
}
result.Add(jsonProperty);
}
return result;
}
}
}