Úvod

Když právní týmy nebo forenzní analytici potřebují prokázat, že dokument nebyl pozměněn, pouhý pohled na viditelný obsah nestačí. Skryté vlastnosti – například autor, datum vytvoření nebo číslo revize – mohou odhalit, kdo soubor upravil a kdy. Detekce těchto jemných změn napříč verzemi dokumentu je častý problém, který často vyžaduje ruční kontrolu každé vlastnosti, což je časově náročné a náchylné k chybám.

GroupDocs.Metadata pro Java poskytuje programatický způsob, jak získat všechna metadata a vypočítat strukturovaný rozdíl mezi dvěma verzemi. V tomto tutoriálu porovnáme tři praktické přístupy: úplný rozdíl metadat, zaměření na změny vlastnictví a analýzu historie revizí. Každá metoda je demonstrována stručným, připraveným k zkopírování kódem a také ukážeme, jak exportovat výsledky do CSV nebo JSON pro auditní zprávy.

Setkal jsem se s tím při revizi smlouvy, kterou během několika měsíců upravovalo více stran; viditelný text vypadal identicky, ale pole vlastnictví se tiše změnila.

Jak zjistit, které metadata se změnila mezi dvěma verzemi dokumentu?

GroupDocs.Metadata načte každý soubor, extrahuje všechna přístupná vlastnosti do mapy a poté iteruje přes klíče, aby klasifikoval přidané, odebrané a upravené položky. Knihovna zvládá vestavěné i vlastní značky, takže získáte kompletní obrázek bez psaní formátově specifických parserů. Výsledkem je objekt MetadataDiff, který můžete dotazovat nebo serializovat pro souladové zprávy.

Požadavky

  • Java 8 nebo novější
  • GroupDocs.Metadata pro Java 24.7 ([dočasná licence][TEMP_LICENSE_URL])
  • Dva soubory dokumentů, které chcete porovnat (např. contract_v1.pdf a contract_v2.pdf)

Instalace

Přidejte závislost přes Maven:

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

Metoda 1 – Úplný rozdíl metadat

Tato metoda extrahuje všechna metadata z obou verzí a hlásí přidané, odebrané i změněné položky.

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

Klíčové body:

  • Komplexní: Zachytí všechny značky, včetně vlastních.
  • Jednoduchá logika map: Není potřeba externí knihovna pro rozdíly.
  • Objekt výsledku: Mapy added, removed a changed jsou připraveny k dalšímu zpracování.

💡 Tip: Použijte, když potřebujete úplný auditní záznam pro regulatorní soulad.

Metoda 2 – Detekce změn vlastnictví

Právní spory často závisí na tom, kdo dokument vytvořil nebo upravil. Tato metoda se zaměřuje na osobní značky jako Creator, Editor, Manager a Company.

// 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 načte pouze relevantní značky:

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;

Klíčové body:

  • Cílené: Zkoumá jen vlastnosti související s identitou.
  • Přehledný výstup: Vrací mapu, kde každý záznam ukazuje hodnoty [old, new].
  • Připravené pro soulad: Ideální pro e‑discovery nebo spory o vlastnictví smluv.

💡 Tip: Kombinujte s úplným rozdílem, pokud potřebujete jak šířku, tak hloubku.

Metoda 3 – Detekce změn v historii revizí

Čísla revizí, časové razítka úprav a data tisku jsou pro koncové uživatele neviditelné, ale jsou klíčová pro forenzní časové osy. Tato metoda izoluje časově související značky.

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 extrahuje časová razítka Modified, Created a Printed:

Map<String, String> result = new LinkedHashMap<>();
try (Metadata metadata = new Metadata(path)) {
    if (metadata.getFileFormat() == FileFormat.Unknown) return