💡 Volledig werkend voorbeeld beschikbaar op GitHub:
metadata-diff-between-docs-using-groupdocs-metadata-dotnet
Introductie
Wanneer een contract tijdens een fusie van eigenaar verandert, moet het juridische team bewijzen dat de eigendomsinformatie van het document – auteur, laatste bewerker, bedrijfsnaam – ongewijzigd is gebleven. Handmatige inspectie van de eigenschappen van elk bestand is tijdrovend en foutgevoelig; één gemiste wijziging kan een compliance‑audit ongeldig maken. GroupDocs.Metadata is een .NET‑bibliotheek die elke ingebedde eigenschap van een document extraheert en vergelijkt, waardoor geautomatiseerde forensische analyse van versie‑naar‑versie‑wijzigingen mogelijk wordt. Deze tutorial laat zien hoe je eigendomsoverdrachten, revisie‑geschiedenisbewerkingen en andere eigenschapswijzigingen tussen twee versies van hetzelfde bestand kunt detecteren en vervolgens de resultaten naar CSV of JSON exporteert voor verdere rapportage.
Ik kwam dit probleem tegen bij het auditen van een set van 1.200 contracten voor een fusie in 2024; de metadata‑audit onthulde 87 onverwachte eigendomswijzigingen die zonder automatisering onzichtbaar zouden zijn gebleven.
In de volgende secties leer je hoe je:
- Alle metadata uit twee documenten extraheert.
- Toegevoegde, verwijderde en gewijzigde eigenschappen identificeert.
- Je richt op eigendom‑ en revisiespecifieke tags.
- Het diff naar CSV of JSON serialiseert voor audit‑trails.
Waarom Metadata Vergelijking Belangrijk Is
Nauwkeurige metadata‑vergelijking is essentieel voor:
- Juridische e‑discovery: Bewijs de herkomst van documenten en detecteer manipulatie.
- Regelgevende compliance: Houd bij wie een bestand heeft gemaakt of aangepast voor GDPR‑ of SOX‑audits.
- Forensisch onderzoek: Spot verborgen revisies of ongeautoriseerde bewerkingen.
Volgens de GroupDocs.Metadata‑documentatie (2024) ontstaat meer dan 30 % van de compliance‑falen door ongedocumenteerde eigenschapswijzigingen.
Vereisten
- .NET 6.0 of later
- GroupDocs.Metadata voor .NET 24.10+ (temporary license)
- Twee documentversies (bijv.
contract_v1.docxencontract_v2.docx)
Installeren via NuGet:
dotnet add package GroupDocs.Metadata
Hoe vergelijk ik metadata tussen twee documentversies?
Antwoord: Laad elk bestand met MetadataFacade, extraheer alle eigenschappen naar dictionaries en doorloop deze vervolgens om een MetadataDiff te bouwen die toegevoegde, verwijderde en gewijzigde items categoriseert. Het diff kan programmatisch worden geïnspecteerd of naar CSV/JSON worden geschreven voor audit‑rapportage.
Volledige‑Eigenschap Diff
Het volgende fragment toont het kern‑diff‑algoritme. Het maakt gebruik van de helper ExtractAllMetadata.Run (zie later) om elke eigenschap uit elke versie te halen.
// 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;
Belangrijkste punten:
MetadataDiffbevat drie dictionaries:Added,Removed,Changed.- Het algoritme werkt in O(n) tijd, geschikt voor bestanden met duizenden eigenschappen.
- Er wordt geen I/O uitgevoerd; de aanroeper beslist hoe het resultaat wordt opgeslagen.
Detectie van Eigendom Wijzigingen
Eigendom‑gerelateerde tags (Author, LastSavedBy, Manager, Company) zijn vaak het meest kritisch voor juridische audits. De onderstaande methode isoleert die tags en rapporteert eventuele verschillen.
// 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;
Belangrijkste punten:
- Maakt gebruik van
Tags.Person.*enTags.Corporate.Company‑predicaten. - Retourneert een dictionary waarbij elke entry oud → nieuw waarden toont.
- Ideaal voor het genereren van een beknopt eigendom‑wijzigingsrapport.
Helper: 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;
Detectie van Revisiegeschiedenis Wijzigingen
Revisie‑gerelateerde metadata (RevisionNumber, TotalEditingTime, LastPrinted) onthult hoe vaak een document is bewerkt. Het fragment hieronder extraheert die eigenschappen en markeert eventuele afwijkingen.
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;
Belangrijkste punten:
- Legt zowel tijdstempels als numerieke revisietellers vast.
- Handig om verborgen bewerkingen te spotten die niet als aparte versies zijn opgeslagen.
Helper: 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;
Exporteren van de Diff naar CSV
Compliance‑teams hebben vaak een spreadsheet‑vriendelijk rapport nodig. De onderstaande methode schrijft de MetadataDiff naar een CSV‑bestand met vier kolommen.
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());
Belangrijkste punten:
CsvEscapequotet velden veilig die komma’s, aanhalingstekens of regeleinden bevatten.- Het resulterende bestand kan direct in Excel worden geopend of in een SIEM worden geladen.
Helper: CsvEscape
if (string.IsNullOrEmpty(s)) return string.Empty;
if (s.Contains(",") || s.Contains("\"") || s.Contains("\n"))
{
return "\"" + s.Replace("\"", "\"\"") + "\"";
}
return s;
Exporteren van de Diff naar JSON
Voor programmatische pipelines heeft een JSON‑payload vaak de voorkeur. De onderstaande methode produceert een stabiel schema met drie top‑level objecten.
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());
Belangrijkste punten:
WriteMapschrijft eenvoudige sleutel‑waarde‑objecten voor de toegevoegde/verwijderde secties.Escapezorgt voor JSON‑compatibele strings.
Helper: 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;
Vergelijkingsmethoden: Wanneer Welke Te Gebruiken
| Methode | Beste Voor | Belangrijkste Voordelen | Beperkingen |
|---|---|---|---|
| Volledige‑eigenschapdiff | Algemene forensische audits | Detecteert elke toegevoegde/verwijderde/gewijzigde eigenschap | Kan een grote diff opleveren voor complexe bestanden |
| Eigendomdetectie | Juridische herkomstcontroles | Richt zich alleen op identiteitsgerelateerde tags | Negeert andere metadata‑wijzigingen |
| Revisiegeschiedenisdetectie | Analyse van bewerkingsactiviteit | Markeert tijdgebaseerde en revisietellers | Vereist dat eigenschappen aanwezig zijn in het bestand |
| CSV‑export | Spreadsheet‑gebaseerde rapportage | Gemakkelijk te openen in Excel, menselijk leesbaar | Beperkt tot platte structuur |
| JSON‑export | Geautomatiseerde pipelines, dashboards | Gestructureerd, machine‑leesbaar | Iets grotere payload |
Kies de volledige‑eigenschapdiff wanneer je een uitgebreide audit nodig hebt; combineer deze met de CSV‑export voor snelle stakeholder‑reviews. Voor geautomatiseerde compliance‑controles kun je de JSON‑output direct in je monitoringsysteem pompen.
Beste Praktijken en Tips
- Dispose
MetadataFacadedirect: plaats het in eenusing‑blok om native resources vrij te geven. - Beperk extractie tot benodigde tags: filteren op
Tags.Person.*ofTags.Time.*vermindert het geheugenverbruik voor grote PDF‑bestanden. - Valideer bestandsformaten:
metadata.FileFormat == FileFormat.Unknownduidt op een niet‑ondersteund of corrupt bestand. - Versieer je audit‑rapporten: neem de bibliotheekversie (
GroupDocs.Metadata 24.10) op in de header van het geëxporteerde bestand voor traceerbaarheid. - Beveiliging: log nooit ruwe eigenschapswaarden die persoonsgegevens kunnen bevatten; maskeer PII vóór opslag.
- Prestaties: voor documenten > 50 MB kun je overwegen de metadata‑extractie te streamen (momenteel niet beschikbaar via de API) of bestanden in parallelle batches te verwerken.
Conclusie
GroupDocs.Metadata biedt een robuuste, programmeerbare manier om elk stuk ingebedde informatie tussen twee documentversies te vergelijken. Door volledige metadata te extraheren, te focussen op eigendom‑ of revisietags, en het diff naar CSV of JSON te exporteren, kun je herhaalbare forensische workflows bouwen die voldoen aan juridische, compliance‑ en beveiligingseisen.
Volgende stappen:
- Verken de volledige lijst met ondersteunde tags om je diff af te stemmen op specifieke regelgevende behoeften.
- Leer hoe je meerdere documenten tegelijk kunt vergelijken (API‑referentie).
- Bekijk extra voorbeeldprojecten op GitHub voor batch‑verwerkingsscenario’s (Examples Repo).