|
|
|
|
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];
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|