You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Connected.Framework/Connected.Interop/Merging/JsonMerger.cs

101 lines
2.9 KiB

2 years ago
using System.Collections;
using System.Reflection;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace Connected.Interop.Merging
{
internal sealed class JsonMerger : Merger
{
public void Merge(object destination, JsonNode? source)
{
if (source is null || destination is null)
return;
foreach (var property in Properties.GetImplementedProperties(destination))
MergeProperty(destination, source, property);
}
private void MergeProperty(object destination, JsonNode source, PropertyInfo property)
{
if (property.FindAttribute<JsonIgnoreAttribute>() is not null)
return;
if (property.PropertyType.IsTypePrimitive())
{
if (!property.CanWrite || source is not JsonObject jo || ResolveJsonProperty(property, jo) is not JsonValue jprop)
return;
if (!TypeConversion.TryConvert(jprop.ToString(), out object? convertedValue, property.PropertyType))
return;
property.SetValue(destination, convertedValue);
}
else if (IsArray(property))
MergeEnumerable(destination, source, property);
else
MergeObject(destination, source, property);
}
private static JsonValue? ResolveJsonProperty(PropertyInfo property, JsonObject json)
{
foreach (var prop in json)
{
if (string.Equals(prop.Key, property.Name, StringComparison.OrdinalIgnoreCase))
return prop.Value as JsonValue;
}
return null;
}
private void MergeEnumerable(object destination, JsonNode source, PropertyInfo property)
{
if (source is not JsonObject jobject)
return;
if (!jobject.ContainsKey(property.Name) || jobject[property.Name] is not JsonArray array || !array.Any())
return;
var value = property.GetValue(destination);
if (value is null && !property.CanWrite)
return;
var addMethod = property.PropertyType.GetMethod(nameof(IList.Add), BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
var instance = addMethod is not null ? Activator.CreateInstance(property.PropertyType) : Array.CreateInstance(property.PropertyType, array.Count);
var elementType = property.PropertyType.GenericTypeArguments[0];
2 years ago
for (var i = 0; i < array.Count; i++)
{
var json = array[i];
var item = Activator.CreateInstance(elementType);
Merge(item, json);
if (addMethod is not null)
addMethod.Invoke(instance, new object[] { item });
else
((Array)instance).SetValue(item, i);
}
property.SetValue(destination, instance);
}
private void MergeObject(object destination, JsonNode source, PropertyInfo property)
{
var value = property.GetValue(destination);
if (value is null && property.CanWrite)
property.SetValue(destination, property.PropertyType.CreateInstance());
value = property.GetValue(destination);
if (value is null)
return;
foreach (var instanceProperty in Properties.GetImplementedProperties(value))
MergeProperty(value, source, instanceProperty);
}
}
}