💡 Full working example available on GitHub:
metadata-diff-between-docs-using-groupdocs-metadata-dotnet

Introducción

Cuando un contrato cambia de manos durante una fusión, el equipo legal debe demostrar que la información de propiedad del documento —autor, último editor, nombre de la empresa— se mantuvo intacta. La inspección manual de las propiedades de cada archivo es tediosa y propensa a errores; un solo cambio pasado por alto puede invalidar una auditoría de cumplimiento. GroupDocs.Metadata es una biblioteca .NET que extrae y compara cada propiedad incrustada de un documento, permitiendo un análisis forense automatizado de los cambios de versión a versión. Este tutorial muestra cómo detectar intercambios de propiedad, ediciones del historial de revisiones y cualquier otra modificación de propiedades entre dos versiones del mismo archivo, y luego exportar los hallazgos a CSV o JSON para informes posteriores.

Me encontré con este problema al auditar un conjunto de 1 200 contratos para una fusión en 2024; la auditoría de metadatos reveló 87 cambios de propiedad inesperados que habrían sido invisibles sin automatización.

En las siguientes secciones aprenderá a:

  • Extraer todos los metadatos de dos documentos.
  • Identificar propiedades añadidas, eliminadas y modificadas.
  • Centrarse en etiquetas de propiedad y de revisión específicas.
  • Serializar la diferencia a CSV o JSON para trazas de auditoría.

Por qué la comparación de metadatos es importante

Una comparación precisa de metadatos es esencial para:

  • e‑discovery legal: Demostrar la procedencia del documento y detectar manipulaciones.
  • Cumplimiento normativo: Rastrear quién creó o modificó un archivo para auditorías GDPR o SOX.
  • Investigaciones forenses: Detectar revisiones ocultas o ediciones no autorizadas.

Según la documentación de GroupDocs.Metadata (2024), más del 30 % de los fallos de cumplimiento provienen de cambios de propiedades no documentados.

Requisitos previos

  • .NET 6.0 o posterior
  • GroupDocs.Metadata para .NET 24.10+ ([licencia temporal][TEMP_LICENSE_URL])
  • Dos versiones del documento (p. ej., contract_v1.docx y contract_v2.docx)

Instalar vía NuGet:

dotnet add package GroupDocs.Metadata

¿Cómo comparo metadatos entre dos versiones de documento?

Respuesta: Cargue cada archivo con MetadataFacade, extraiga todas las propiedades a diccionarios y luego itere para construir un MetadataDiff que categorice las entradas añadidas, eliminadas y modificadas. La diferencia puede inspeccionarse programáticamente o escribirse a CSV/JSON para informes de auditoría.

Diferencia de todas las propiedades

El siguiente fragmento muestra el algoritmo central de diferencia. Utiliza el ayudante ExtractAllMetadata.Run (ver más adelante) para obtener todas las propiedades de cada versión.

// Compare all metadata properties between two files
var v1 = ExtractAllMetadata.Run(pathV1);
var v2 = ExtractAllMetadata.Run(pathV2);
var diff = new MetadataDiff();

// Detect added and changed properties
foreach (var kvp in v2)
{
    if (!v1.ContainsKey(kvp.Key))
    {
        diff.Added[kvp.Key] = kvp.Value; // New property in v2
    }
    else if (v1[kvp.Key] != kvp.Value)
    {
        diff.Changed[kvp.Key] = (v1[kvp.Key], kvp.Value); // Value changed
    }
}

// Detect removed properties
foreach (var kvp in v1)
{
    if (!v2.ContainsKey(kvp.Key))
    {
        diff.Removed[kvp.Key] = kvp.Value; // Property missing in v2
    }
}

return diff;

Puntos clave:

  • MetadataDiff contiene tres diccionarios: Added, Removed, Changed.
  • El algoritmo se ejecuta en tiempo O(n), adecuado para archivos con miles de propiedades.
  • No se realiza I/O; el llamador decide cómo persistir el resultado.

Detección de cambios de propiedad

Las etiquetas relacionadas con la propiedad (Author, LastSavedBy, Manager, Company) suelen ser las más críticas para auditorías legales. El método a continuación aísla esas etiquetas y reporta cualquier diferencia.

// Get ownership‑related properties from each version
var v1Values = GetOwnershipProperties(pathV1);
var v2Values = GetOwnershipProperties(pathV2);
var changes = new Dictionary<string, (string, string)>();

var allKeys = new HashSet<string>(v1Values.Keys);
foreach (var key in v2Values.Keys) allKeys.Add(key);

foreach (var key in allKeys)
{
    var oldV = v1Values.TryGetValue(key, out var o) ? o : "<missing>";
    var newV = v2Values.TryGetValue(key, out var n) ? n : "<missing>";
    if (oldV != newV)
    {
        changes[key] = (oldV, newV);
    }
}

return changes;

Puntos clave:

  • Utiliza los predicados Tags.Person.* y Tags.Corporate.Company.
  • Devuelve un diccionario donde cada entrada muestra antiguo → nuevo.
  • Ideal para generar un informe conciso de cambios de propiedad.

Ayudante: GetOwnershipProperties

var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
using (var metadata = new MetadataFacade(path))
{
    if (metadata.FileFormat == FileFormat.Unknown) return dict;

    var props = metadata.FindProperties(p =>
        p.Tags.Contains(Tags.Person.Creator) ||
        p.Tags.Contains(Tags.Person.Editor) ||
        p.Tags.Contains(Tags.Person.Manager) ||
        p.Tags.Contains(Tags.Corporate.Company));

    foreach (var p in props)
    {
        dict[p.Name] = p.InterpretedValue?.ToString() ?? p.Value?.ToString() ?? string.Empty;
    }
}
return dict;

Detección de cambios en el historial de revisiones

Los metadatos relacionados con revisiones (RevisionNumber, TotalEditingTime, LastPrinted) revelan cuántas veces se editó un documento. El fragmento a continuación extrae esas propiedades y resalta cualquier delta.

var v1 = GetRevisionProperties(pathV1);
var v2 = GetRevisionProperties(pathV2);
var changes = new Dictionary<string, (string, string)>();

var allKeys = new HashSet<string>(v1.Keys);
foreach (var k in v2.Keys) allKeys.Add(k);

foreach (var key in allKeys)
{
    var oldV = v1.TryGetValue(key, out var o) ? o : "<missing>";
    var newV = v2.TryGetValue(key, out var n) ? n : "<missing>";
    if (oldV != newV)
    {
        changes[key] = (oldV, newV);
    }
}

return changes;

Puntos clave:

  • Captura tanto marcas de tiempo como contadores numéricos de revisión.
  • Útil para detectar ediciones ocultas que no se guardaron como versiones separadas.

Ayudante: GetRevisionProperties

var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
using (var metadata = new MetadataFacade(path))
{
    if (metadata.FileFormat == FileFormat.Unknown) return dict;

    var props = metadata.FindProperties(p =>
        p.Tags.Contains(Tags.Time.Modified) ||
        p.Tags.Contains(Tags.Time.Created) ||
        p.Tags.Contains(Tags.Time.Printed) ||
        p.Name != null && (p.Name.Contains("Revision") || p.Name