Giriş

Hukuk ekipleri veya adli analistler bir belgenin değiştirilmediğini kanıtlamaları gerektiğinde, yalnızca görünen içeriğe bakmak yeterli değildir. Yazar, oluşturulma tarihi veya revizyon numarası gibi gizli özellikler, dosyaya kim ne zaman dokunduğunu ortaya çıkarabilir. Bu ince değişiklikleri belge sürümleri arasında tespit etmek, genellikle her özelliğin manuel olarak incelenmesini gerektiren, zaman alıcı ve hata yapmaya açık bir sorundur.

GroupDocs.Metadata for Java, her meta veri alanını programatik olarak çıkarmayı ve iki sürüm arasında yapılandırılmış bir fark (diff) hesaplamayı sağlar. Bu öğreticide üç pratik yaklaşımı karşılaştıracağız: tam meta veri farkı, odaklanmış sahiplik değişikliği tespiti ve revizyon geçmişi analizi. Her yöntem, kısa ve kopyala‑yapıştırılabilir kod örnekleriyle gösterilecek ve sonuçların denetim raporlaması için CSV ya da JSON formatına nasıl dışa aktarılacağı da gösterilecektir.

Bunu, aylar boyunca birden fazla tarafça düzenlenen bir sözleşmeyi incelerken fark ettim; görünen metin aynıydı, ancak sahiplik alanları sessizce değişmişti.

İki belge sürümü arasında hangi meta veri alanlarının değiştiğini nasıl anlayabilirim?

GroupDocs.Metadata, her dosyayı yükler, erişilebilir tüm özellikleri bir haritaya (map) çıkarır ve ardından eklemeleri, kaldırmaları ve değişiklikleri sınıflandırmak için anahtarlar üzerinde döner. Kütüphane, yerleşik ve özel etiketleri (tags) yönetir, böylece format‑özel ayrıştırıcılar yazmadan eksiksiz bir görünüm elde edersiniz. Sonuç, uyumluluk raporları için sorgulayabileceğiniz veya serileştirebileceğiniz bir MetadataDiff nesnesidir.

Önkoşullar

  • Java 8 veya üzeri
  • GroupDocs.Metadata for Java 24.7 (geçici lisans)
  • Karşılaştırmak istediğiniz iki belge dosyası (ör. contract_v1.pdf ve contract_v2.pdf)

Kurulum

Maven üzerinden bağımlılığı ekleyin:

<dependency>
    <groupId>com.groupdocs</groupId>
    <artifactId>groupdocs-metadata</artifactId>
    <version>24.7</version>
</dependency>

Yöntem 1 – Tam Meta Veri Farkı

Bu yöntem, her iki sürümden tüm meta veri özelliklerini çıkarır ve eklenen, kaldırılan ve değiştirilen girişleri raporlar.

// CompareMetadataSets.run – returns a MetadataDiff object
Map<String, String> v1 = ExtractAllMetadata.run(pathV1);
Map<String, String> v2 = ExtractAllMetadata.run(pathV2);
MetadataDiff diff = new MetadataDiff();

// Detect added and changed properties
for (Map.Entry<String, String> e : v2.entrySet()) {
    String key = e.getKey();
    String val = e.getValue();
    if (!v1.containsKey(key)) {
        diff.added.put(key, val);               // New property in v2
    } else if (!v1.get(key).equals(val)) {
        diff.changed.put(key, new String[]{v1.get(key), val}); // Value changed
    }
}
// Detect removed properties
for (Map.Entry<String, String> e : v1.entrySet()) {
    if (!v2.containsKey(e.getKey())) {
        diff.removed.put(e.getKey(), e.getValue());
    }
}
return diff;

Temel noktalar:

  • Kapsamlı: Özel etiketler dahil tüm etiketleri yakalar.
  • Basit harita mantığı: Harici bir diff kütüphanesi gerekmez.
  • Sonuç nesnesi: added, removed ve changed haritaları daha fazla işleme hazırdır.

💡 İpucu: Düzenleyici uyumluluk için tam bir denetim izi gerektiğinde bunu kullanın.

Yöntem 2 – Sahiplik Değişikliklerini Tespit Etme

Hukuki anlaşmazlıklar genellikle belgenin kim tarafından oluşturulduğu veya düzenlendiği üzerine odaklanır. Bu yöntem, Creator, Editor, Manager ve Company gibi kişi‑ilişkili etiketlere odaklanır.

// DetectOwnershipChanges.run – returns a map of changed ownership fields
Map<String, String> v1 = readOwnership(pathV1);
Map<String, String> v2 = readOwnership(pathV2);
Set<String> allKeys = new HashSet<>(v1.keySet());
allKeys.addAll(v2.keySet());
Map<String, String[]> changes = new LinkedHashMap<>();
for (String key : allKeys) {
    String oldVal = v1.getOrDefault(key, "<missing>");
    String newVal = v2.getOrDefault(key, "<missing>");
    if (!oldVal.equals(newVal)) {
        changes.put(key, new String[]{oldVal, newVal});
    }
}
return changes;

readOwnership yalnızca ilgili etiketleri çeker:

Map<String, String> result = new LinkedHashMap<>();
try (Metadata metadata = new Metadata(path)) {
    if (metadata.getFileFormat() == FileFormat.Unknown) return result;
    for (MetadataProperty p : metadata.findProperties(
            new ContainsTagSpecification(Tags.getPerson().getCreator())
                .or(new ContainsTagSpecification(Tags.getPerson().getEditor()))
                .or(new ContainsTagSpecification(Tags.getPerson().getManager()))
                .or(new ContainsTagSpecification(Tags.getCorporate().getCompany())))) {
        String value = "";
        if (p.getValue() != null && p.getValue().getRawValue() != null) {
            value = String.valueOf(p.getValue().getRawValue());
        }
        result.put(p.getName(), value);
    }
}
return result;

Temel noktalar:

  • Hedefli: Yalnızca kimlik‑taşıyan özellikler incelenir.
  • Açık çıktı: Her giriş [eski, yeni] değerlerini gösteren bir harita döndürür.
  • Uyumluluk‑hazır: e‑keşif (e‑discovery) veya sözleşme‑sahipliği anlaşmazlıkları için mükemmeldir.

💡 İpucu: Hem genişlik hem de derinlik istiyorsanız tam farkla birleştirin.

Yöntem 3 – Revizyon Geçmişi Değişikliklerini Tespit Etme

Revizyon numaraları, düzenleme zaman damgaları ve baskı tarihleri son kullanıcılar için görünmez, ancak adli zaman çizelgeleri için kritiktir. Bu yöntem zaman‑ilişkili etiketleri izole eder.

Map<String, String> v1 = readRevision(pathV1);
Map<String, String> v2 = readRevision(pathV2);
Set<String> allKeys = new HashSet<>(v1.keySet());
allKeys.addAll(v2.keySet());
Map<String, String[]> changes = new LinkedHashMap<>();
for (String key : allKeys) {
    String oldVal = v1.getOrDefault(key, "<missing>");
    String newVal = v2.getOrDefault(key, "<missing>");
    if (!oldVal.equals(newVal)) {
        changes.put(key, new String[]{oldVal, newVal});
    }
}
return changes;

readRevision Modified, Created ve Printed zaman damgalarını çıkarır:

Map<String, String> result = new LinkedHashMap<>();
try (Metadata metadata = new Metadata(path)) {
    if (metadata.getFileFormat() == FileFormat.Unknown) return result;
    for (MetadataProperty p : metadata.findProperties(
            new ContainsTagSpecification(Tags.getTime().getModified())
                .or(new ContainsTagSpecification(Tags.getTime().getCreated()))
                .or(new ContainsTagSpecification(Tags.getTime().getPrinted())))) {
        String value = "";
        if (p.getValue() != null && p.getValue().getRawValue() != null) {
            value = String.valueOf(p.getValue().getRawValue());
        }
        result.put(p.getName(), value);
    }
}
return result;

Temel noktalar:

  • Zaman çizelgesi yeniden oluşturma: Dosyanın kaç kez düzenlendiğini veya yazdırıldığını gösterir.
  • Sayısal farklar: Şüpheli hızlı revizyonları tespit etmek için yararlıdır.
  • Hafif: Yalnızca üç etiket sorgulanır, bu da yürütmeyi hızlı tutar.

💡 İpucu: Belgenin belirli bir son tarihten sonra değiştirilmediğini kanıtlamanız gerektiğinde bunu kullanın.

Yöntemleri Karşılaştırma: Ne Zaman Hangi Yöntemi Kullanmalı

Yöntem En İyi Kullanım Durumu Temel Avantajlar Kısıtlamalar
Tam Meta Veri Farkı Tam denetim, düzenleyici uyumluluk Her özelliği, özel etiketler dahil yakalar Çok büyük dosyalar için daha yüksek bellek tüketimi
Sahiplik Değişikliği Tespiti Hukuki sahiplik anlaşmazlıkları, e‑keşif Kişi‑ilişkili alanlara odaklanır, okunması kolay Diğer faydalı meta verileri göz ardı eder
Revizyon Geçmişi Tespiti Zaman çizelgesi adli incelemesi, değişiklik‑log doğrulama Zaman damgaları ve revizyon numaralarını izole eder İçerik‑seviyesi değişiklikleri göstermez

Uyumluluk hedefinize en uygun yöntemi seçin. Çoğu durumda bir kombinasyon—tam farkı çalıştırıp ardından sahiplik veya revizyon bölümlerine dalmak—en fazla içgörüyü sağlar.

Farkı Dışa Aktarma

Bir MetadataDiff elde ettikten sonra bulguları paylaşmanız genellikle gerekir. Aşağıda iki basit dışa aktarıcı bulunmaktadır.

CSV Dışa Aktarma

StringBuilder sb = new StringBuilder();
sb.append("change_type,property,old_value,new_value\n");
for (Map.Entry<String, String> e : diff.added.entrySet()) {
    sb.append("added,").append(esc(e.getKey()))
      .append(",,").append(esc(e.getValue())).append("\n");
}
for (Map.Entry<String, String> e : diff.removed.entrySet()) {
    sb.append("removed,").append(esc(e.getKey()))
      .append(",").append(esc(e.getValue()))
      .append(",\n");
}
for (Map.Entry<String, String[]> e : diff.changed.entrySet()) {
    sb.append("changed,").append(esc(e.getKey()))
      .append(",").append(esc(e.getValue()[0]))
      .append(",").append(esc(e.getValue()[1])).append("\n");
}
Files.write(Paths.get(outputPath), sb.toString().getBytes(StandardCharsets.UTF_8));

JSON Dışa Aktarma

StringBuilder sb = new StringBuilder();
sb.append("{\n");
sb.append("  \"added\": {\n");
writeMap(sb, diff.added);
sb.append("  },\n");
sb.append("  \"removed\": {\n");
writeMap(sb, diff.removed);
sb.append("  },\n");
sb.append("  \"changed\": {\n");
int i = 0;
for (Map.Entry<String, String[]> e : diff.changed.entrySet()) {
    String comma = ++i < diff.changed.size() ? "," : "";
    sb.append("    \"").append(escape(e.getKey()))
      .append("\": { \"from\": \"")
      .append(escape(e.getValue()[0])).append("\", \"to\": \"")
      .append(escape(e.getValue()[1])).append("\" }")
      .append(comma).append("\n");
}
sb.append("  }\n");
sb.append("}\n");
Files.write(Paths.get(outputPath), sb.toString().getBytes(StandardCharsets.UTF_8));

Her iki dışa aktarıcı da, virgül ve tırnak işaretlerini güvenli bir şekilde işleyen yardımcı metodları (esc, escape, writeMap) kullanır.

En İyi Uygulamalar ve İpuçları

  • Fark kapsamını sınırlayın: Büyük PDF’lerde işleme süresini azaltmak için farkı yalnızca sahiplik veya revizyon etiketleriyle sınırlayın.
  • Dosya formatını doğrulayın: Özellikler üzerinde döngüye girmeden önce her zaman metadata.getFileFormat() != FileFormat.Unknown kontrol edin.
  • Kaynakları serbest bırakın: try‑with‑resources (try (Metadata metadata = new Metadata(path)) { … }) kullanarak yerel tutamaçları (handles) serbest bırakın.
  • Sürüm tutarlılığı: Her iki belgenin de aynı dosya formatı sürümünden olduğundan emin olun; DOCX ile eski DOC karışımı yanıltıcı sonuçlar verebilir.
  • Güvenlik: Ham meta veri değerlerini halka açık API’lerde sanitizasyon olmadan asla ortaya çıkarmayın; CSV/JSON yazarken esc/escape yardımcılarını kullanın.
  • Performans: Toplu veri alımı için CSV’ye dışa aktarın; JSON ise insan‑okunur denetim günlükleri için daha iyidir.

Sonuç

GroupDocs.Metadata for Java, meta veri adli incelemelerini son derece basitleştirir. Tam fark, sahiplik tespiti ve revizyon‑geçmişi analiz yöntemlerini birleştirerek, gizli değişiklikleri ortaya çıkaran, yasal delil desteği sağlayan ve uyumluluk gereksinimlerini karşılayan sağlam bir denetim hattı oluşturabilirsiniz. CSV ya da JSON olarak dışa aktarmak, raporlama araçları veya veri‑deposu (data‑warehouse) boru hatlarıyla sorunsuz entegrasyon sağlar.

Sonraki adımlar:

  • Özel meta veri filtreleri için gelişmiş etiket (tag) spesifikasyonlarını keşfedin GroupDocs.Metadata Java docs.
  • Toplu olarak birden fazla belgeyi karşılaştırmayı öğrenin API reference.
  • Resmi örnek projelere göz atarak uç‑uç (end‑to‑end) uygulamaları inceleyin GitHub examples.

Ek Kaynaklar