💡 Tam çalışan örnek GitHub’da mevcuttur:
metadata-diff-between-docs-using-groupdocs-metadata-dotnet
Giriş
Bir birleşme sırasında bir sözleşme el değiştirdiğinde, hukuk ekibi belgenin sahiplik bilgilerinin – yazar, son düzenleyici, şirket adı – değişmediğini kanıtlamak zorundadır. Her dosyanın özelliklerini manuel olarak incelemek zahmetli ve hata yapmaya açıktır; tek bir kaçırılan değişiklik uyum denetimini geçersiz kılabilir. GroupDocs.Metadata, bir .NET kütüphanesi olarak bir belgenin gömülü tüm özelliklerini çıkarır ve karşılaştırır, sürüm‑sürüm değişikliklerinin otomatik adli analizini mümkün kılar. Bu öğreticide aynı dosyanın iki sürümü arasındaki sahiplik değişimlerini, revizyon‑geçmişi düzenlemelerini ve diğer tüm özellik değişikliklerini nasıl tespit edeceğinizi, ardından bulguları CSV veya JSON olarak dışa aktararak raporlama sürecine nasıl dahil edeceğinizi göstereceğiz.
Bu sorunu, 2024 birleşmesi için 1.200 sözleşme setini denetlerken yaşadım; metadata denetimi, otomasyon olmadan görünmez olabilecek 87 beklenmedik sahiplik değişikliği ortaya çıkardı.
Aşağıdaki bölümlerde şunları öğreneceksiniz:
- İki belgeden tüm metadata’yı çıkarma.
- Eklenen, kaldırılan ve değişen özellikleri belirleme.
- Sahiplik ve revizyon‑özel etiketlere odaklanma.
- Denetim izleri için farkı CSV veya JSON olarak serileştirme.
Why Metadata Comparison Matters
Doğru metadata karşılaştırması şunlar için kritiktir:
- Yasal e‑keşif: Belge kökenini kanıtlayın ve sahteciliği tespit edin.
- Regülasyon uyumu: GDPR veya SOX denetimleri için dosyayı kimin oluşturduğunu veya değiştirdiğini izleyin.
- Adli araştırmalar: Gizli revizyonları veya yetkisiz düzenlemeleri ortaya çıkarın.
GroupDocs.Metadata belgelerine (2024) göre, uyumsuzluk hatalarının %30 ‘undan fazlası belgelenmemiş özellik değişikliklerinden kaynaklanmaktadır.
Prerequisites
- .NET 6.0 veya üzeri
- GroupDocs.Metadata for .NET 24.10+ (geçici lisans)
- İki belge sürümü (ör.
contract_v1.docxvecontract_v2.docx)
NuGet üzerinden kurun:
dotnet add package GroupDocs.Metadata
İki belge sürümü arasında metadata nasıl karşılaştırılır?
Cevap: Her dosyayı MetadataFacade ile yükleyin, tüm özellikleri sözlüklere çıkarın, ardından eklenen, kaldırılan ve değişen girdileri sınıflandıran bir MetadataDiff oluşturmak için yineleyin. Fark programatik olarak incelenebilir veya denetim raporlaması için CSV/JSON olarak yazılabilir.
Tam‑Özellik Farkı
Aşağıdaki kod parçacığı temel fark algoritmasını gösterir. Her sürümden tüm özellikleri çekmek için yardımcı ExtractAllMetadata.Run (daha sonra) kullanılır.
// 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;
Ana noktalar:
MetadataDiffüç sözlük tutar:Added,Removed,Changed.- Algoritma O(n) zaman karmaşıklığına sahiptir, binlerce özellik içeren dosyalar için uygundur.
- Giriş/Çıkış (I/O) yapılmaz; sonucu nasıl kalıcı hale getireceğiniz size kalmıştır.
Sahiplik Değişikliklerini Algılama
Sahiplik‑ile ilgili etiketler (Author, LastSavedBy, Manager, Company) genellikle yasal denetimler için en kritik olanlardır. Aşağıdaki yöntem bu etiketleri izole eder ve farkları raporlar.
// 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;
Ana noktalar:
Tags.Person.*veTags.Corporate.Companyöngörüleri kullanılır.- Her giriş eski → yeni değerlerini gösteren bir sözlük döndürür.
- Kısa bir sahiplik‑değişikliği raporu oluşturmak için idealdir.
Yardımcı: 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;
Revizyon Geçmişi Değişikliklerini Algılama
Revizyon‑ile ilgili metadata (RevisionNumber, TotalEditingTime, LastPrinted) belgenin kaç kez düzenlendiğini gösterir. Aşağıdaki kod bu özellikleri çıkarır ve farkları vurgular.
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;
Ana noktalar:
- Hem zaman damgalarını hem de sayısal revizyon sayaçlarını yakalar.
- Ayrı ayrı kaydedilmemiş gizli düzenlemeleri tespit etmek için kullanışlıdır.
Yardımcı: 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;
Farkı CSV’ye Aktarma
Uyum ekipleri genellikle bir elektronik tablo‑dostu rapor ister. Aşağıdaki yöntem MetadataDiff i dört sütunlu bir CSV dosyasına yazar.
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());
Ana noktalar:
CsvEscape, virgül veya satır sonu içeren alanları güvenli bir şekilde tırnak içine alır.- Oluşan dosya doğrudan Excel’de açılabilir veya bir SIEM’e yüklenebilir.
Yardımcı: CsvEscape
if (string.IsNullOrEmpty(s)) return string.Empty;
if (s.Contains(",") || s.Contains("\"") || s.Contains("\n"))
{
return "\"" + s.Replace("\"", "\"\"") + "\"";
}
return s;
Farkı JSON’a Aktarma
Programatik boru hatları için JSON genellikle tercih edilir. Aşağıdaki yöntem üç üst‑seviye nesne içeren sabit bir şema üretir.
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());
Ana noktalar:
WriteMap, eklenen/kaldırılan bölümler için basit anahtar‑değer nesneleri yazar.Escape, JSON uyumlu dizgileri garanti eder.
Yardımcı: 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;
Yöntemleri Karşılaştırma: Hangi Durumda Hangi Yöntemi Kullanmalı
| Yöntem | En İyi Kullanım Durumu | Temel Avantajlar | Sınırlamalar |
|---|---|---|---|
| Full‑Property Diff | Genel adli denetimler | Eklenen/kaldırılan/değişen tüm özellikleri tespit eder | Karmaşık dosyalar için büyük fark çıktısı üretebilir |
| Ownership Detection | Yasal köken kontrolleri | Sadece kimlik‑taşıyan etiketlere odaklanır | Diğer metadata değişikliklerini görmez |
| Revision History Detection | Düzenleme aktivitesi analizi | Zaman‑bazlı ve revizyon sayaçlarını vurgular | Özelliklerin dosyada bulunması gerekir |
| CSV Export | Elektronik tablo‑tabanlı raporlama | Excel’de kolay açılır, insan‑okunur | Düz yapı nedeniyle sınırlı |
| JSON Export | Otomatik boru hatları, panolar | Yapılandırılmış, makine‑okunur | Biraz daha büyük payload |
Tam‑özellik farkını, kapsamlı bir denetim gerektiğinde seçin; hızlı paydaş incelemeleri için CSV dışa aktarımını birleştirin. Otomatik uyum kontrolleri için JSON çıktısını doğrudan izleme sisteminize yönlendirin.
En İyi Uygulamalar ve İpuçları
MetadataFacade‘i hemen serbest bırakın: yerel kaynakları temizlemek içinusingbloğu kullanın.- Gerekli etiketlerle sınırlı çıkarım yapın:
Tags.Person.*veyaTags.Time.*filtrelemesi, büyük PDF’lerde bellek kullanımını azaltır. - Dosya formatlarını doğrulayın:
metadata.FileFormat == FileFormat.Unknowndesteklenmeyen veya bozuk bir dosyayı gösterir. - Denetim raporlarınızı sürümleyin: dışa aktarılan dosya başlığına kütüphane sürümünü (
GroupDocs.Metadata 24.10) ekleyerek izlenebilirliği sağlayın. - Güvenlik: Kişisel veri içerebilecek ham özellik değerlerini asla loglamayın; saklamadan önce PII’yı maskeleyin.
- Performans: 50 MB üzerindeki belgeler için metadata çıkarımını akış (stream) olarak yapmayı (API’da henüz mevcut değil) düşünün veya dosyaları paralel toplu işlerde işleyin.
Sonuç
GroupDocs.Metadata, iki belge sürümü arasındaki gömülü tüm bilgileri karşılaştırmak için sağlam ve programatik bir yol sunar. Tam metadata çıkarımı, sahiplik veya revizyon etiketlerine odaklanma ve farkı CSV veya JSON olarak dışa aktarma sayesinde, yasal, uyum ve güvenlik gereksinimlerini karşılayan tekrarlanabilir adli iş akışları oluşturabilirsiniz.
Sonraki adımlar:
- Regülasyon ihtiyaçlarınıza göre farkı özelleştirmek için desteklenen etiketlerin tam listesine göz atın.
- Birden fazla belgeyi aynı anda karşılaştırmayı keşfedin (API referansı).
- Toplu işleme senaryoları için GitHub’daki ek örnek projelere bakın (Örnek Deposu).