Files
2026-06-23 19:03:17 +02:00

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"
};
}
}