💡 Voll funktionsfähiges Beispiel auf GitHub verfügbar: metadata-diff-between-docs-using-groupdocs-metadata-dotnet

Einleitung

Wenn ein Vertrag während einer Fusion den Besitzer wechselt, muss das Rechts‑Team nachweisen, dass die Eigentumsinformationen des Dokuments – Autor, letzter Bearbeiter, Firmenname – unverändert geblieben sind. Die manuelle Überprüfung der Eigenschaften jeder Datei ist mühsam und fehleranfällig; eine einzige übersehene Änderung kann ein Compliance‑Audit ungültig machen. GroupDocs.Metadata ist eine .NET‑Bibliothek, die jede eingebettete Eigenschaft eines Dokuments extrahiert und vergleicht und so eine automatisierte forensische Analyse von Versions‑zu‑Versions‑Änderungen ermöglicht. Dieses Tutorial zeigt, wie man Eigentumswechsel, Änderungen der Revisionshistorie und andere Eigenschaftsänderungen zwischen zwei Versionen derselben Datei erkennt und die Ergebnisse anschließend in CSV oder JSON für nachgelagerte Berichte exportiert.

Ich stieß auf dieses Problem, als ich im Rahmen einer Fusion im Jahr 2024 einen Satz von 1 200 Verträgen prüfte; das Metadaten‑Audit deckte 87 unerwartete Eigentumsänderungen auf, die ohne Automatisierung unsichtbar geblieben wären.

In den folgenden Abschnitten lernen Sie, wie Sie:

  • Alle Metadaten aus zwei Dokumenten extrahieren.
  • Hinzugefügte, entfernte und geänderte Eigenschaften identifizieren.
  • Sich auf Eigentums‑ und revisionsspezifische Tags konzentrieren.
  • Den Unterschied in CSV oder JSON für Prüfpfade serialisieren.

Warum der Vergleich von Metadaten wichtig ist

Eine genaue Metadaten‑Vergleich ist unerlässlich für:

  • Legal e‑discovery: Nachweis der Dokumentenherkunft und Erkennung von Manipulationen.
  • Regulatorische Compliance: Verfolgung, wer eine Datei erstellt oder geändert hat, für GDPR‑ oder SOX‑Audits.
  • Forensische Untersuchungen: Aufdecken versteckter Revisionen oder unautorisierter Änderungen.

Laut der GroupDocs.Metadata‑Dokumentation (2024) resultieren über 30 % der Compliance‑Fehler aus undokumentierten Eigenschaftsänderungen.

Voraussetzungen

  • .NET 6.0 oder höher
  • GroupDocs.Metadata für .NET 24.10+ (temporary license)
  • Zwei Dokumentversionen (z. B. contract_v1.docx und contract_v2.docx)

Installation über NuGet:

dotnet add package GroupDocs.Metadata

Wie vergleiche ich Metadaten zwischen zwei Dokumentversionen?

Antwort: Laden Sie jede Datei mit MetadataFacade, extrahieren Sie alle Eigenschaften in Dictionaries und iterieren Sie anschließend, um ein MetadataDiff zu erstellen, das hinzugefügte, entfernte und geänderte Einträge kategorisiert. Der Diff kann programmgesteuert inspiziert oder für Audit‑Berichte in CSV/JSON geschrieben werden.

Voll‑Property Diff

Das folgende Snippet demonstriert den Kern‑Diff‑Algorithmus. Es verwendet die Hilfsfunktion ExtractAllMetadata.Run (siehe weiter unten), um jede Eigenschaft aus jeder Version zu ziehen.

// 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;

Wichtige Punkte:

  • MetadataDiff enthält drei Dictionaries: Added, Removed, Changed.
  • Der Algorithmus läuft in O(n)-Zeit, geeignet für Dateien mit Tausenden von Eigenschaften.
  • Es wird kein I/O durchgeführt; Aufrufer entscheiden, wie das Ergebnis gespeichert wird.

Erkennen von Eigentumsänderungen

Eigentumsbezogene Tags (Author, LastSavedBy, Manager, Company) sind häufig die kritischsten für rechtliche Audits. Die nachstehende Methode isoliert diese Tags und meldet alle Unterschiede.

// 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;

Wichtige Punkte:

  • Verwendet Tags.Person.* und Tags.Corporate.Company Prädikate.
  • Gibt ein Dictionary zurück, bei dem jeder Eintrag alt → neu Werte zeigt.
  • Ideal für die Erstellung eines prägnanten Eigentums‑Änderungsberichts.

Hilfsfunktion: 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;

Erkennen von Änderungen der Revisionshistorie

Revisionsbezogene Metadaten (RevisionNumber, TotalEditingTime, LastPrinted) zeigen, wie oft ein Dokument bearbeitet wurde. Das folgende Snippet extrahiert diese Eigenschaften und hebt alle Abweichungen hervor.

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;

Wichtige Punkte:

  • Erfasst sowohl Zeitstempel als auch numerische Revisionszähler.
  • Nützlich, um versteckte Bearbeitungen zu entdecken, die nicht als separate Versionen gespeichert wurden.

Hilfsfunktion: 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.Contains("EditTime") || p.Name.Contains("EditingTime")));

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

Exportieren des Diff nach CSV

Compliance‑Teams benötigen häufig einen tabellenfreundlichen Bericht. Die folgende Methode schreibt das MetadataDiff in eine CSV‑Datei mit vier Spalten.

var sb = new StringBuilder();
sb.AppendLine("change_type,property,old_value,new_value");

foreach (var kvp in diff.Added)
{
    sb.AppendLine($"added,{CsvEscape(kvp.Key)},,{CsvEscape(kvp.Value)}");
}
foreach (var kvp in diff.Removed)
{
    sb.AppendLine($"removed,{CsvEscape(kvp.Key)},{CsvEscape(kvp.Value)},");
}
foreach (var kvp in diff.Changed)
{
    sb.AppendLine($"changed,{CsvEscape(kvp.Key)},{CsvEscape(kvp.Value.OldValue)},{CsvEscape(kvp.Value.NewValue)}");
}

File.WriteAllText(outputPath, sb.ToString());

Wichtige Punkte:

  • CsvEscape quotiert Felder, die Kommas oder Zeilenumbrüche enthalten, sicher.
  • Die resultierende Datei kann direkt in Excel geöffnet oder in ein SIEM geladen werden.

Hilfsfunktion: CsvEscape

if (string.IsNullOrEmpty(s)) return string.Empty;
if (s.Contains(",") || s.Contains("\"") || s.Contains("\n"))
{
    return "\"" + s.Replace("\"", "\"\"") + "\"";
}
return s;

Exportieren des Diff nach JSON

Für programmgesteuerte Pipelines wird häufig ein JSON‑Payload bevorzugt. Die nachstehende Methode erzeugt ein stabiles Schema mit drei Top‑Level‑Objekten.

var sb = new StringBuilder();
sb.AppendLine("{");
sb.AppendLine("  \"added\": {");
WriteMap(sb, diff.Added);
sb.AppendLine("  },");
sb.AppendLine("  \"removed\": {");
WriteMap(sb, diff.Removed);
sb.AppendLine("  },");
sb.AppendLine("  \"changed\": {");
var changedItems = 0;
foreach (var kvp in diff.Changed)
{
    var comma = ++changedItems < diff.Changed.Count ? "," : string.Empty;
    sb.AppendLine($"    \"{Escape(kvp.Key)}\": {{ \"from\": \"{Escape(kvp.Value.OldValue)}\", \"to\": \"{Escape(kvp.Value.NewValue)}\" }}{comma}");
}
sb.AppendLine("  }");
sb.AppendLine("}");

File.WriteAllText(outputPath, sb.ToString());

Wichtige Punkte:

  • WriteMap schreibt einfache Schlüssel‑Wert‑Objekte für die Abschnitte hinzugefügt/entfernt.
  • Escape sorgt für JSON‑kompatible Zeichenketten.

Hilfsfunktion: WriteMap & Escape

var i = 0;
foreach (var kvp in map)
{
    var comma = ++i < map.Count ? "," : string.Empty;
    sb.AppendLine($"    \"{Escape(kvp.Key)}\": \"{Escape(kvp.Value)}\"{comma}");
}
return s?.Replace("\\", "\\\\").Replace("\"", "\\\"") ?? string.Empty;

Vergleich von Methoden: Wann welche verwenden

Methode Am besten für Wesentliche Vorteile Einschränkungen
Full‑Property Diff Allgemeine forensische Audits Erkennt jede hinzugefügte/entfernte/geänderte Eigenschaft Kann bei komplexen Dateien großen Diff erzeugen
Ownership Detection Rechtliche Herkunftsprüfungen Fokusiert nur auf identitätsbezogene Tags Ignoriert andere Metadatenänderungen
Revision History Detection Analyse von Bearbeitungsaktivitäten Hebt zeitbasierte und Revisionszähler hervor Erfordert, dass die Eigenschaften im Dokument vorhanden sind
CSV Export Berichtserstellung in Tabellenform Einfach in Excel zu öffnen, menschenlesbar Auf flache Struktur beschränkt
JSON Export Automatisierte Pipelines, Dashboards Strukturiert, maschinenlesbar Etwas größerer Payload

Wählen Sie den Voll‑Property‑Diff, wenn Sie ein umfassendes Audit benötigen; kombinieren Sie ihn mit dem CSV‑Export für schnelle Stakeholder‑Reviews. Für automatisierte Compliance‑Checks leiten Sie die JSON‑Ausgabe direkt in Ihr Monitoring‑System weiter.

Beste Praktiken und Tipps

  • MetadataFacade sofort freigeben: In einem using‑Block einbetten, um native Ressourcen zu löschen.
  • Extraktion auf benötigte Tags beschränken: Filtern nach Tags.Person.* oder Tags.Time.* reduziert den Speicherverbrauch bei großen PDFs.
  • Dateiformate validieren: metadata.FileFormat == FileFormat.Unknown weist auf ein nicht unterstütztes oder beschädigtes Dokument hin.
  • Audit‑Berichte versionieren: Bibliotheksversion (GroupDocs.Metadata 24.10) im Dateikopf vermerken für Nachvollziehbarkeit.
  • Sicherheit: Rohwerte, die personenbezogene Daten enthalten können, niemals unverändert protokollieren; PII vor dem Persistieren maskieren.
  • Performance: Bei Dokumenten > 50 MB ggf. Streaming‑Extraktion (derzeit nicht über die API verfügbar) in Betracht ziehen oder Dateien in parallelen Batches verarbeiten.

Fazit

GroupDocs.Metadata bietet einen robusten, programmgesteuerten Weg, jede eingebettete Information zwischen zwei Dokumentversionen zu vergleichen. Durch das Extrahieren vollständiger Metadaten, das Fokussieren auf Eigentums‑ oder Revisions‑Tags und das Exportieren des Diff nach CSV oder JSON können Sie wiederholbare forensische Workflows erstellen, die rechtlichen, Compliance‑ und Sicherheitsanforderungen entsprechen.

Nächste Schritte:

Weitere Ressourcen