198 lines
7.0 KiB
C#
Executable File
198 lines
7.0 KiB
C#
Executable File
using audit.Model;
|
|
|
|
namespace audit_logs.Utils;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using MongoDB.Bson;
|
|
using Newtonsoft.Json.Linq;
|
|
using Utils;
|
|
|
|
public class JsonComparer
|
|
{
|
|
public List<AuditRecord.Change> GetDifferences(object? originalJson, object? modifiedJson)
|
|
{
|
|
var changes = new List<AuditRecord.Change>();
|
|
|
|
// Convertir los objetos a cadenas JSON; usar "{}" si el objeto es null
|
|
var originalJsonString = originalJson != null
|
|
? JsonConverter.ConvertObjectToJson(originalJson)
|
|
: "{}";
|
|
var modifiedJsonString = modifiedJson != null
|
|
? JsonConverter.ConvertObjectToJson(modifiedJson)
|
|
: "{}";
|
|
|
|
// Intentar parsear a JObject solo si el JSON es un objeto
|
|
var originalObj = ParseAsJObject(originalJsonString);
|
|
var modifiedObj = ParseAsJObject(modifiedJsonString);
|
|
//eliminamos aquellas claves que contengan null, por ejemplo "allergies": [
|
|
// {
|
|
// "optionType": null,
|
|
// "name": "Sí",
|
|
// "iconDefault": "icCheck",
|
|
// "iconLight": null,
|
|
// "iconDark": null,
|
|
// "color": null,
|
|
// "bgColor": null,
|
|
// "description": null,
|
|
// "initDate": null,
|
|
// "endDate": null
|
|
// }
|
|
// ]
|
|
|
|
if (originalObj != null)
|
|
{
|
|
originalObj = JsonClean.CleanJObject(originalObj);
|
|
}
|
|
if (modifiedObj != null)
|
|
{
|
|
modifiedObj = JsonClean.CleanJObject(modifiedObj);
|
|
}
|
|
// Comparar los objetos JSON
|
|
CompareJTokens(originalObj, modifiedObj, changes, string.Empty);
|
|
return changes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Intenta parsear una cadena JSON como JObject. Devuelve null si no es un objeto.
|
|
/// </summary>
|
|
/// <param name="json">La cadena JSON a validar.</param>
|
|
/// <returns>Un JObject si es válido, de lo contrario null.</returns>
|
|
private static JObject? ParseAsJObject(string json)
|
|
{
|
|
try
|
|
{
|
|
var token = JToken.Parse(json);
|
|
return token as JObject; // Devuelve null si no es un JObject
|
|
}
|
|
catch
|
|
{
|
|
return null; // Si ocurre un error, no es un JSON válido
|
|
}
|
|
}
|
|
|
|
|
|
private void CompareJTokens(JToken original, JToken modified, List<AuditRecord.Change> changes, string path)
|
|
{
|
|
foreach (var property in modified.Children<JProperty>())
|
|
{
|
|
var fieldPath = string.IsNullOrEmpty(path) ? property.Name : $"{path}.{property.Name}";
|
|
var originalValue = original[property.Name];
|
|
var modifiedValue = property.Value;
|
|
// Ignorar campos donde ambos valores son null
|
|
if (originalValue == null && modifiedValue.Type == JTokenType.Null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (originalValue == null)
|
|
{
|
|
// Campo nuevo
|
|
changes.Add(new AuditRecord.Change
|
|
{
|
|
Field = fieldPath,
|
|
OldValue = BsonNull.Value,
|
|
NewValue = ConvertToBsonValue(modifiedValue),
|
|
ValueType = GetBsonType(modifiedValue)
|
|
});
|
|
}
|
|
else if (!JToken.DeepEquals(originalValue, modifiedValue))
|
|
{
|
|
if (originalValue.Type == JTokenType.Object && modifiedValue.Type == JTokenType.Object)
|
|
{
|
|
// Comparar objetos anidados
|
|
CompareJTokens(originalValue, modifiedValue, changes, fieldPath);
|
|
}
|
|
else
|
|
{
|
|
// Campo modificado
|
|
changes.Add(new AuditRecord.Change
|
|
{
|
|
Field = fieldPath,
|
|
OldValue = ConvertToBsonValue(originalValue),
|
|
NewValue = ConvertToBsonValue(modifiedValue),
|
|
ValueType = GetBsonType(modifiedValue)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Campos eliminados
|
|
foreach (var property in original.Children<JProperty>())
|
|
{
|
|
var fieldPath = string.IsNullOrEmpty(path) ? property.Name : $"{path}.{property.Name}";
|
|
if (modified[property.Name] == null)
|
|
{
|
|
changes.Add(new AuditRecord.Change
|
|
{
|
|
Field = fieldPath,
|
|
OldValue = ConvertToBsonValue(property.Value),
|
|
NewValue = BsonNull.Value,
|
|
ValueType = GetBsonType(property.Value)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/*private BsonValue ConvertToBsonValue(JToken token)
|
|
{
|
|
return token.Type switch
|
|
{
|
|
JTokenType.String => new BsonString(token.ToString()),
|
|
JTokenType.Integer => new BsonInt32(token.ToObject<int>()),
|
|
JTokenType.Float => new BsonDouble(token.ToObject<double>()),
|
|
JTokenType.Boolean => new BsonBoolean(token.ToObject<bool>()),
|
|
JTokenType.Date => new BsonDateTime(token.ToObject<DateTime>()),
|
|
JTokenType.Null => BsonNull.Value,
|
|
JTokenType.Object => BsonDocument.Parse(token.ToString()),
|
|
JTokenType.Array => new BsonArray(token.ToObject<List<BsonValue>>()),
|
|
_ => new BsonString(token.ToString())
|
|
};
|
|
}*/
|
|
private BsonValue ConvertToBsonValue(JToken token)
|
|
{
|
|
switch (token.Type)
|
|
{
|
|
case JTokenType.String:
|
|
return new BsonString(token.ToString());
|
|
case JTokenType.Integer:
|
|
return BsonValue.Create(token.ToObject<int>());
|
|
case JTokenType.Float:
|
|
return BsonValue.Create(token.ToObject<double>());
|
|
case JTokenType.Boolean:
|
|
return new BsonBoolean(token.ToObject<bool>());
|
|
case JTokenType.Date:
|
|
return new BsonDateTime(token.ToObject<DateTime>());
|
|
case JTokenType.Null:
|
|
return BsonNull.Value;
|
|
case JTokenType.Object:
|
|
return BsonDocument.Parse(token.ToString());
|
|
case JTokenType.Array:
|
|
var array = new BsonArray();
|
|
foreach (var item in token.Children())
|
|
{
|
|
array.Add(ConvertToBsonValue(item));
|
|
}
|
|
return array;
|
|
default:
|
|
return new BsonString(token.ToString()); // Trata cualquier otro tipo como string
|
|
}
|
|
}
|
|
|
|
|
|
private string GetBsonType(JToken token)
|
|
{
|
|
return token.Type switch
|
|
{
|
|
JTokenType.String => "string",
|
|
JTokenType.Integer => "int",
|
|
JTokenType.Float => "double",
|
|
JTokenType.Boolean => "bool",
|
|
JTokenType.Date => "date",
|
|
JTokenType.Null => "null",
|
|
JTokenType.Object => "object",
|
|
JTokenType.Array => "array",
|
|
_ => "unknown"
|
|
};
|
|
}
|
|
}
|